39e13346428ad0962c3a83bbe0f0228b19e30397
[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     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<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>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * @abstract
227  * @children Roo.bootstrap.Component
228  * Bootstrap Component base class
229  * @cfg {String} cls css class
230  * @cfg {String} style any extra css
231  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
233  * @cfg {string} dataId cutomer id
234  * @cfg {string} name Specifies name attribute
235  * @cfg {string} tooltip  Text for the tooltip
236  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
237  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238  
239  * @constructor
240  * Do not use directly - it does not do anything..
241  * @param {Object} config The config object
242  */
243
244
245
246 Roo.bootstrap.Component = function(config){
247     Roo.bootstrap.Component.superclass.constructor.call(this, config);
248        
249     this.addEvents({
250         /**
251          * @event childrenrendered
252          * Fires when the children have been rendered..
253          * @param {Roo.bootstrap.Component} this
254          */
255         "childrenrendered" : true
256         
257         
258         
259     });
260     
261     
262 };
263
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
265     
266     
267     allowDomMove : false, // to stop relocations in parent onRender...
268     
269     cls : false,
270     
271     style : false,
272     
273     autoCreate : false,
274     
275     tooltip : null,
276     /**
277      * Initialize Events for the element
278      */
279     initEvents : function() { },
280     
281     xattr : false,
282     
283     parentId : false,
284     
285     can_build_overlaid : true,
286     
287     container_method : false,
288     
289     dataId : false,
290     
291     name : false,
292     
293     parent: function() {
294         // returns the parent component..
295         return Roo.ComponentMgr.get(this.parentId)
296         
297         
298     },
299     
300     // private
301     onRender : function(ct, position)
302     {
303        // Roo.log("Call onRender: " + this.xtype);
304         
305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306         
307         if(this.el){
308             if (this.el.attr('xtype')) {
309                 this.el.attr('xtypex', this.el.attr('xtype'));
310                 this.el.dom.removeAttribute('xtype');
311                 
312                 this.initEvents();
313             }
314             
315             return;
316         }
317         
318          
319         
320         var cfg = Roo.apply({},  this.getAutoCreate());
321         
322         cfg.id = this.id || Roo.id();
323         
324         // fill in the extra attributes 
325         if (this.xattr && typeof(this.xattr) =='object') {
326             for (var i in this.xattr) {
327                 cfg[i] = this.xattr[i];
328             }
329         }
330         
331         if(this.dataId){
332             cfg.dataId = this.dataId;
333         }
334         
335         if (this.cls) {
336             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337         }
338         
339         if (this.style) { // fixme needs to support more complex style data.
340             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341         }
342         
343         if(this.name){
344             cfg.name = this.name;
345         }
346         
347         this.el = ct.createChild(cfg, position);
348         
349         if (this.tooltip) {
350             this.tooltipEl().attr('tooltip', this.tooltip);
351         }
352         
353         if(this.tabIndex !== undefined){
354             this.el.dom.setAttribute('tabIndex', this.tabIndex);
355         }
356         
357         this.initEvents();
358         
359     },
360     /**
361      * Fetch the element to add children to
362      * @return {Roo.Element} defaults to this.el
363      */
364     getChildContainer : function()
365     {
366         return this.el;
367     },
368     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
369     {
370         return Roo.get(document.body);
371     },
372     
373     /**
374      * Fetch the element to display the tooltip on.
375      * @return {Roo.Element} defaults to this.el
376      */
377     tooltipEl : function()
378     {
379         return this.el;
380     },
381         
382     addxtype  : function(tree,cntr)
383     {
384         var cn = this;
385         
386         cn = Roo.factory(tree);
387         //Roo.log(['addxtype', cn]);
388            
389         cn.parentType = this.xtype; //??
390         cn.parentId = this.id;
391         
392         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393         if (typeof(cn.container_method) == 'string') {
394             cntr = cn.container_method;
395         }
396         
397         
398         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
399         
400         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
401         
402         var build_from_html =  Roo.XComponent.build_from_html;
403           
404         var is_body  = (tree.xtype == 'Body') ;
405           
406         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
407           
408         var self_cntr_el = Roo.get(this[cntr](false));
409         
410         // do not try and build conditional elements 
411         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412             return false;
413         }
414         
415         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417                 return this.addxtypeChild(tree,cntr, is_body);
418             }
419             
420             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421                 
422             if(echild){
423                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424             }
425             
426             Roo.log('skipping render');
427             return cn;
428             
429         }
430         
431         var ret = false;
432         if (!build_from_html) {
433             return false;
434         }
435         
436         // this i think handles overlaying multiple children of the same type
437         // with the sam eelement.. - which might be buggy..
438         while (true) {
439             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
440             
441             if (!echild) {
442                 break;
443             }
444             
445             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446                 break;
447             }
448             
449             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
450         }
451        
452         return ret;
453     },
454     
455     
456     addxtypeChild : function (tree, cntr, is_body)
457     {
458         Roo.debug && Roo.log('addxtypeChild:' + cntr);
459         var cn = this;
460         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461         
462         
463         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464                     (typeof(tree['flexy:foreach']) != 'undefined');
465           
466     
467         
468         skip_children = false;
469         // render the element if it's not BODY.
470         if (!is_body) {
471             
472             // if parent was disabled, then do not try and create the children..
473             if(!this[cntr](true)){
474                 tree.items = [];
475                 return tree;
476             }
477            
478             cn = Roo.factory(tree);
479            
480             cn.parentType = this.xtype; //??
481             cn.parentId = this.id;
482             
483             var build_from_html =  Roo.XComponent.build_from_html;
484             
485             
486             // does the container contain child eleemnts with 'xtype' attributes.
487             // that match this xtype..
488             // note - when we render we create these as well..
489             // so we should check to see if body has xtype set.
490             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
491                
492                 var self_cntr_el = Roo.get(this[cntr](false));
493                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
494                 if (echild) { 
495                     //Roo.log(Roo.XComponent.build_from_html);
496                     //Roo.log("got echild:");
497                     //Roo.log(echild);
498                 }
499                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500                 // and are not displayed -this causes this to use up the wrong element when matching.
501                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502                 
503                 
504                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
506                   
507                   
508                   
509                     cn.el = echild;
510                   //  Roo.log("GOT");
511                     //echild.dom.removeAttribute('xtype');
512                 } else {
513                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514                     Roo.debug && Roo.log(self_cntr_el);
515                     Roo.debug && Roo.log(echild);
516                     Roo.debug && Roo.log(cn);
517                 }
518             }
519            
520             
521            
522             // if object has flexy:if - then it may or may not be rendered.
523             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
524                 // skip a flexy if element.
525                 Roo.debug && Roo.log('skipping render');
526                 Roo.debug && Roo.log(tree);
527                 if (!cn.el) {
528                     Roo.debug && Roo.log('skipping all children');
529                     skip_children = true;
530                 }
531                 
532              } else {
533                  
534                 // actually if flexy:foreach is found, we really want to create 
535                 // multiple copies here...
536                 //Roo.log('render');
537                 //Roo.log(this[cntr]());
538                 // some elements do not have render methods.. like the layouts...
539                 /*
540                 if(this[cntr](true) === false){
541                     cn.items = [];
542                     return cn;
543                 }
544                 */
545                 cn.render && cn.render(this[cntr](true));
546                 
547              }
548             // then add the element..
549         }
550          
551         // handle the kids..
552         
553         var nitems = [];
554         /*
555         if (typeof (tree.menu) != 'undefined') {
556             tree.menu.parentType = cn.xtype;
557             tree.menu.triggerEl = cn.el;
558             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559             
560         }
561         */
562         if (!tree.items || !tree.items.length) {
563             cn.items = nitems;
564             //Roo.log(["no children", this]);
565             
566             return cn;
567         }
568          
569         var items = tree.items;
570         delete tree.items;
571         
572         //Roo.log(items.length);
573             // add the items..
574         if (!skip_children) {    
575             for(var i =0;i < items.length;i++) {
576               //  Roo.log(['add child', items[i]]);
577                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
578             }
579         }
580         
581         cn.items = nitems;
582         
583         //Roo.log("fire childrenrendered");
584         
585         cn.fireEvent('childrenrendered', this);
586         
587         return cn;
588     },
589     
590     /**
591      * Set the element that will be used to show or hide
592      */
593     setVisibilityEl : function(el)
594     {
595         this.visibilityEl = el;
596     },
597     
598      /**
599      * Get the element that will be used to show or hide
600      */
601     getVisibilityEl : function()
602     {
603         if (typeof(this.visibilityEl) == 'object') {
604             return this.visibilityEl;
605         }
606         
607         if (typeof(this.visibilityEl) == 'string') {
608             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
609         }
610         
611         return this.getEl();
612     },
613     
614     /**
615      * Show a component - removes 'hidden' class
616      */
617     show : function()
618     {
619         if(!this.getVisibilityEl()){
620             return;
621         }
622          
623         this.getVisibilityEl().removeClass(['hidden','d-none']);
624         
625         this.fireEvent('show', this);
626         
627         
628     },
629     /**
630      * Hide a component - adds 'hidden' class
631      */
632     hide: function()
633     {
634         if(!this.getVisibilityEl()){
635             return;
636         }
637         
638         this.getVisibilityEl().addClass(['hidden','d-none']);
639         
640         this.fireEvent('hide', this);
641         
642     }
643 });
644
645  /*
646  * - LGPL
647  *
648  * element
649  * 
650  */
651
652 /**
653  * @class Roo.bootstrap.Element
654  * @extends Roo.bootstrap.Component
655  * @children Roo.bootstrap.Component
656  * Bootstrap Element class (basically a DIV used to make random stuff )
657  * 
658  * @cfg {String} html contents of the element
659  * @cfg {String} tag tag of the element
660  * @cfg {String} cls class of the element
661  * @cfg {Boolean} preventDefault (true|false) default false
662  * @cfg {Boolean} clickable (true|false) default false
663  * @cfg {String} role default blank - set to button to force cursor pointer
664  
665  * 
666  * @constructor
667  * Create a new Element
668  * @param {Object} config The config object
669  */
670
671 Roo.bootstrap.Element = function(config){
672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
673     
674     this.addEvents({
675         // raw events
676         /**
677          * @event click
678          * When a element is chick
679          * @param {Roo.bootstrap.Element} this
680          * @param {Roo.EventObject} e
681          */
682         "click" : true 
683         
684       
685     });
686 };
687
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
689     
690     tag: 'div',
691     cls: '',
692     html: '',
693     preventDefault: false, 
694     clickable: false,
695     tapedTwice : false,
696     role : false,
697     
698     getAutoCreate : function(){
699         
700         var cfg = {
701             tag: this.tag,
702             // cls: this.cls, double assign in parent class Component.js :: onRender
703             html: this.html
704         };
705         if (this.role !== false) {
706             cfg.role = this.role;
707         }
708         
709         return cfg;
710     },
711     
712     initEvents: function() 
713     {
714         Roo.bootstrap.Element.superclass.initEvents.call(this);
715         
716         if(this.clickable){
717             this.el.on('click', this.onClick, this);
718         }
719         
720         
721     },
722     
723     onClick : function(e)
724     {
725         if(this.preventDefault){
726             e.preventDefault();
727         }
728         
729         this.fireEvent('click', this, e); // why was this double click before?
730     },
731     
732     
733     
734
735     
736     
737     getValue : function()
738     {
739         return this.el.dom.innerHTML;
740     },
741     
742     setValue : function(value)
743     {
744         this.el.dom.innerHTML = value;
745     }
746    
747 });
748
749  
750
751  /*
752  * - LGPL
753  *
754  * dropable area
755  * 
756  */
757
758 /**
759  * @class Roo.bootstrap.DropTarget
760  * @extends Roo.bootstrap.Element
761  * Bootstrap DropTarget class
762  
763  * @cfg {string} name dropable name
764  * 
765  * @constructor
766  * Create a new Dropable Area
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.DropTarget = function(config){
771     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772     
773     this.addEvents({
774         // raw events
775         /**
776          * @event click
777          * When a element is chick
778          * @param {Roo.bootstrap.Element} this
779          * @param {Roo.EventObject} e
780          */
781         "drop" : true
782     });
783 };
784
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
786     
787     
788     getAutoCreate : function(){
789         
790          
791     },
792     
793     initEvents: function() 
794     {
795         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
797             ddGroup: this.name,
798             listeners : {
799                 drop : this.dragDrop.createDelegate(this),
800                 enter : this.dragEnter.createDelegate(this),
801                 out : this.dragOut.createDelegate(this),
802                 over : this.dragOver.createDelegate(this)
803             }
804             
805         });
806         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
807     },
808     
809     dragDrop : function(source,e,data)
810     {
811         // user has to decide how to impliment this.
812         Roo.log('drop');
813         Roo.log(this);
814         //this.fireEvent('drop', this, source, e ,data);
815         return false;
816     },
817     
818     dragEnter : function(n, dd, e, data)
819     {
820         // probably want to resize the element to match the dropped element..
821         Roo.log("enter");
822         this.originalSize = this.el.getSize();
823         this.el.setSize( n.el.getSize());
824         this.dropZone.DDM.refreshCache(this.name);
825         Roo.log([n, dd, e, data]);
826     },
827     
828     dragOut : function(value)
829     {
830         // resize back to normal
831         Roo.log("out");
832         this.el.setSize(this.originalSize);
833         this.dropZone.resetConstraints();
834     },
835     
836     dragOver : function()
837     {
838         // ??? do nothing?
839     }
840    
841 });
842
843  
844
845  /*
846  * - LGPL
847  *
848  * Body
849  *
850  */
851
852 /**
853  * @class Roo.bootstrap.Body
854  * @extends Roo.bootstrap.Component
855  * @builder-top
856  * @children Roo.bootstrap.Component
857  * @parent none
858  * Bootstrap Body class
859  *
860  * @constructor
861  * Create a new body
862  * @param {Object} config The config object
863  */
864
865 Roo.bootstrap.Body = function(config){
866
867     config = config || {};
868
869     Roo.bootstrap.Body.superclass.constructor.call(this, config);
870     this.el = Roo.get(config.el ? config.el : document.body );
871     if (this.cls && this.cls.length) {
872         Roo.get(document.body).addClass(this.cls);
873     }
874 };
875
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
877
878     is_body : true,// just to make sure it's constructed?
879
880         autoCreate : {
881         cls: 'container'
882     },
883     onRender : function(ct, position)
884     {
885        /* Roo.log("Roo.bootstrap.Body - onRender");
886         if (this.cls && this.cls.length) {
887             Roo.get(document.body).addClass(this.cls);
888         }
889         // style??? xttr???
890         */
891     }
892
893
894
895
896 });
897 /*
898  * - LGPL
899  *
900  * button group
901  * 
902  */
903
904
905 /**
906  * @class Roo.bootstrap.ButtonGroup
907  * @extends Roo.bootstrap.Component
908  * Bootstrap ButtonGroup class
909  * @children Roo.bootstrap.Button Roo.bootstrap.Form
910  * 
911  * @cfg {String} size lg | sm | xs (default empty normal)
912  * @cfg {String} align vertical | justified  (default none)
913  * @cfg {String} direction up | down (default down)
914  * @cfg {Boolean} toolbar false | true
915  * @cfg {Boolean} btn true | false
916  * 
917  * 
918  * @constructor
919  * Create a new Input
920  * @param {Object} config The config object
921  */
922
923 Roo.bootstrap.ButtonGroup = function(config){
924     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
925 };
926
927 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
928     
929     size: '',
930     align: '',
931     direction: '',
932     toolbar: false,
933     btn: true,
934
935     getAutoCreate : function(){
936         var cfg = {
937             cls: 'btn-group',
938             html : null
939         };
940         
941         cfg.html = this.html || cfg.html;
942         
943         if (this.toolbar) {
944             cfg = {
945                 cls: 'btn-toolbar',
946                 html: null
947             };
948             
949             return cfg;
950         }
951         
952         if (['vertical','justified'].indexOf(this.align)!==-1) {
953             cfg.cls = 'btn-group-' + this.align;
954             
955             if (this.align == 'justified') {
956                 console.log(this.items);
957             }
958         }
959         
960         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
961             cfg.cls += ' btn-group-' + this.size;
962         }
963         
964         if (this.direction == 'up') {
965             cfg.cls += ' dropup' ;
966         }
967         
968         return cfg;
969     },
970     /**
971      * Add a button to the group (similar to NavItem API.)
972      */
973     addItem : function(cfg)
974     {
975         var cn = new Roo.bootstrap.Button(cfg);
976         //this.register(cn);
977         cn.parentId = this.id;
978         cn.onRender(this.el, null);
979         return cn;
980     }
981    
982 });
983
984  /*
985  * - LGPL
986  *
987  * button
988  * 
989  */
990
991 /**
992  * @class Roo.bootstrap.Button
993  * @extends Roo.bootstrap.Component
994  * Bootstrap Button class
995  * @cfg {String} html The button content
996  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
997  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
998  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
999  * @cfg {String} size (lg|sm|xs)
1000  * @cfg {String} tag (a|input|submit)
1001  * @cfg {String} href empty or href
1002  * @cfg {Boolean} disabled default false;
1003  * @cfg {Boolean} isClose default false;
1004  * @cfg {String} glyphicon depricated - use fa
1005  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1006  * @cfg {String} badge text for badge
1007  * @cfg {String} theme (default|glow)  
1008  * @cfg {Boolean} inverse dark themed version
1009  * @cfg {Boolean} toggle is it a slidy toggle button
1010  * @cfg {Boolean} pressed   default null - if the button ahs active state
1011  * @cfg {String} ontext text for on slidy toggle state
1012  * @cfg {String} offtext text for off slidy toggle state
1013  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1014  * @cfg {Boolean} removeClass remove the standard class..
1015  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1016  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1017  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1018
1019  * @constructor
1020  * Create a new button
1021  * @param {Object} config The config object
1022  */
1023
1024
1025 Roo.bootstrap.Button = function(config){
1026     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1027     
1028     this.addEvents({
1029         // raw events
1030         /**
1031          * @event click
1032          * When a button is pressed
1033          * @param {Roo.bootstrap.Button} btn
1034          * @param {Roo.EventObject} e
1035          */
1036         "click" : true,
1037         /**
1038          * @event dblclick
1039          * When a button is double clicked
1040          * @param {Roo.bootstrap.Button} btn
1041          * @param {Roo.EventObject} e
1042          */
1043         "dblclick" : true,
1044          /**
1045          * @event toggle
1046          * After the button has been toggles
1047          * @param {Roo.bootstrap.Button} btn
1048          * @param {Roo.EventObject} e
1049          * @param {boolean} pressed (also available as button.pressed)
1050          */
1051         "toggle" : true
1052     });
1053 };
1054
1055 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1056     html: false,
1057     active: false,
1058     weight: '',
1059     badge_weight: '',
1060     outline : false,
1061     size: '',
1062     tag: 'button',
1063     href: '',
1064     disabled: false,
1065     isClose: false,
1066     glyphicon: '',
1067     fa: '',
1068     badge: '',
1069     theme: 'default',
1070     inverse: false,
1071     
1072     toggle: false,
1073     ontext: 'ON',
1074     offtext: 'OFF',
1075     defaulton: true,
1076     preventDefault: true,
1077     removeClass: false,
1078     name: false,
1079     target: false,
1080     group : false,
1081      
1082     pressed : null,
1083      
1084     
1085     getAutoCreate : function(){
1086         
1087         var cfg = {
1088             tag : 'button',
1089             cls : 'roo-button',
1090             html: ''
1091         };
1092         
1093         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1094             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1095             this.tag = 'button';
1096         } else {
1097             cfg.tag = this.tag;
1098         }
1099         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1100         
1101         if (this.toggle == true) {
1102             cfg={
1103                 tag: 'div',
1104                 cls: 'slider-frame roo-button',
1105                 cn: [
1106                     {
1107                         tag: 'span',
1108                         'data-on-text':'ON',
1109                         'data-off-text':'OFF',
1110                         cls: 'slider-button',
1111                         html: this.offtext
1112                     }
1113                 ]
1114             };
1115             // why are we validating the weights?
1116             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1117                 cfg.cls +=  ' ' + this.weight;
1118             }
1119             
1120             return cfg;
1121         }
1122         
1123         if (this.isClose) {
1124             cfg.cls += ' close';
1125             
1126             cfg["aria-hidden"] = true;
1127             
1128             cfg.html = "&times;";
1129             
1130             return cfg;
1131         }
1132              
1133         
1134         if (this.theme==='default') {
1135             cfg.cls = 'btn roo-button';
1136             
1137             //if (this.parentType != 'Navbar') {
1138             this.weight = this.weight.length ?  this.weight : 'default';
1139             //}
1140             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1141                 
1142                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1143                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1144                 cfg.cls += ' btn-' + outline + weight;
1145                 if (this.weight == 'default') {
1146                     // BC
1147                     cfg.cls += ' btn-' + this.weight;
1148                 }
1149             }
1150         } else if (this.theme==='glow') {
1151             
1152             cfg.tag = 'a';
1153             cfg.cls = 'btn-glow roo-button';
1154             
1155             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1156                 
1157                 cfg.cls += ' ' + this.weight;
1158             }
1159         }
1160    
1161         
1162         if (this.inverse) {
1163             this.cls += ' inverse';
1164         }
1165         
1166         
1167         if (this.active || this.pressed === true) {
1168             cfg.cls += ' active';
1169         }
1170         
1171         if (this.disabled) {
1172             cfg.disabled = 'disabled';
1173         }
1174         
1175         if (this.items) {
1176             Roo.log('changing to ul' );
1177             cfg.tag = 'ul';
1178             this.glyphicon = 'caret';
1179             if (Roo.bootstrap.version == 4) {
1180                 this.fa = 'caret-down';
1181             }
1182             
1183         }
1184         
1185         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1186          
1187         //gsRoo.log(this.parentType);
1188         if (this.parentType === 'Navbar' && !this.parent().bar) {
1189             Roo.log('changing to li?');
1190             
1191             cfg.tag = 'li';
1192             
1193             cfg.cls = '';
1194             cfg.cn =  [{
1195                 tag : 'a',
1196                 cls : 'roo-button',
1197                 html : this.html,
1198                 href : this.href || '#'
1199             }];
1200             if (this.menu) {
1201                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1202                 cfg.cls += ' dropdown';
1203             }   
1204             
1205             delete cfg.html;
1206             
1207         }
1208         
1209        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1210         
1211         if (this.glyphicon) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'span',
1217                     cls: 'glyphicon glyphicon-' + this.glyphicon
1218                 }
1219             ];
1220         }
1221         if (this.fa) {
1222             cfg.html = ' ' + cfg.html;
1223             
1224             cfg.cn = [
1225                 {
1226                     tag: 'i',
1227                     cls: 'fa fas fa-' + this.fa
1228                 }
1229             ];
1230         }
1231         
1232         if (this.badge) {
1233             cfg.html += ' ';
1234             
1235             cfg.tag = 'a';
1236             
1237 //            cfg.cls='btn roo-button';
1238             
1239             cfg.href=this.href;
1240             
1241             var value = cfg.html;
1242             
1243             if(this.glyphicon){
1244                 value = {
1245                     tag: 'span',
1246                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1247                     html: this.html
1248                 };
1249             }
1250             if(this.fa){
1251                 value = {
1252                     tag: 'i',
1253                     cls: 'fa fas fa-' + this.fa,
1254                     html: this.html
1255                 };
1256             }
1257             
1258             var bw = this.badge_weight.length ? this.badge_weight :
1259                 (this.weight.length ? this.weight : 'secondary');
1260             bw = bw == 'default' ? 'secondary' : bw;
1261             
1262             cfg.cn = [
1263                 value,
1264                 {
1265                     tag: 'span',
1266                     cls: 'badge badge-' + bw,
1267                     html: this.badge
1268                 }
1269             ];
1270             
1271             cfg.html='';
1272         }
1273         
1274         if (this.menu) {
1275             cfg.cls += ' dropdown';
1276             cfg.html = typeof(cfg.html) != 'undefined' ?
1277                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1278         }
1279         
1280         if (cfg.tag !== 'a' && this.href !== '') {
1281             throw "Tag must be a to set href.";
1282         } else if (this.href.length > 0) {
1283             cfg.href = this.href;
1284         }
1285         
1286         if(this.removeClass){
1287             cfg.cls = '';
1288         }
1289         
1290         if(this.target){
1291             cfg.target = this.target;
1292         }
1293         
1294         return cfg;
1295     },
1296     initEvents: function() {
1297        // Roo.log('init events?');
1298 //        Roo.log(this.el.dom);
1299         // add the menu...
1300         
1301         if (typeof (this.menu) != 'undefined') {
1302             this.menu.parentType = this.xtype;
1303             this.menu.triggerEl = this.el;
1304             this.addxtype(Roo.apply({}, this.menu));
1305         }
1306
1307
1308         if (this.el.hasClass('roo-button')) {
1309              this.el.on('click', this.onClick, this);
1310              this.el.on('dblclick', this.onDblClick, this);
1311         } else {
1312              this.el.select('.roo-button').on('click', this.onClick, this);
1313              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1314              
1315         }
1316         // why?
1317         if(this.removeClass){
1318             this.el.on('click', this.onClick, this);
1319         }
1320         
1321         if (this.group === true) {
1322              if (this.pressed === false || this.pressed === true) {
1323                 // nothing
1324             } else {
1325                 this.pressed = false;
1326                 this.setActive(this.pressed);
1327             }
1328             
1329         }
1330         
1331         this.el.enableDisplayMode();
1332         
1333     },
1334     onClick : function(e)
1335     {
1336         if (this.disabled) {
1337             return;
1338         }
1339         
1340         Roo.log('button on click ');
1341         if(this.preventDefault){
1342             e.preventDefault();
1343         }
1344         
1345         if (this.group) {
1346             if (this.pressed) {
1347                 // do nothing -
1348                 return;
1349             }
1350             this.setActive(true);
1351             var pi = this.parent().items;
1352             for (var i = 0;i < pi.length;i++) {
1353                 if (this == pi[i]) {
1354                     continue;
1355                 }
1356                 if (pi[i].el.hasClass('roo-button')) {
1357                     pi[i].setActive(false);
1358                 }
1359             }
1360             this.fireEvent('click', this, e);            
1361             return;
1362         }
1363         
1364         if (this.pressed === true || this.pressed === false) {
1365             this.toggleActive(e);
1366         }
1367         
1368         
1369         this.fireEvent('click', this, e);
1370     },
1371     onDblClick: function(e)
1372     {
1373         if (this.disabled) {
1374             return;
1375         }
1376         if(this.preventDefault){
1377             e.preventDefault();
1378         }
1379         this.fireEvent('dblclick', this, e);
1380     },
1381     /**
1382      * Enables this button
1383      */
1384     enable : function()
1385     {
1386         this.disabled = false;
1387         this.el.removeClass('disabled');
1388         this.el.dom.removeAttribute("disabled");
1389     },
1390     
1391     /**
1392      * Disable this button
1393      */
1394     disable : function()
1395     {
1396         this.disabled = true;
1397         this.el.addClass('disabled');
1398         this.el.attr("disabled", "disabled")
1399     },
1400      /**
1401      * sets the active state on/off, 
1402      * @param {Boolean} state (optional) Force a particular state
1403      */
1404     setActive : function(v) {
1405         
1406         this.el[v ? 'addClass' : 'removeClass']('active');
1407         this.pressed = v;
1408     },
1409      /**
1410      * toggles the current active state 
1411      */
1412     toggleActive : function(e)
1413     {
1414         this.setActive(!this.pressed); // this modifies pressed...
1415         this.fireEvent('toggle', this, e, this.pressed);
1416     },
1417      /**
1418      * get the current active state
1419      * @return {boolean} true if it's active
1420      */
1421     isActive : function()
1422     {
1423         return this.el.hasClass('active');
1424     },
1425     /**
1426      * set the text of the first selected button
1427      */
1428     setText : function(str)
1429     {
1430         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1431     },
1432     /**
1433      * get the text of the first selected button
1434      */
1435     getText : function()
1436     {
1437         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1438     },
1439     
1440     setWeight : function(str)
1441     {
1442         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1443         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1444         this.weight = str;
1445         var outline = this.outline ? 'outline-' : '';
1446         if (str == 'default') {
1447             this.el.addClass('btn-default btn-outline-secondary');        
1448             return;
1449         }
1450         this.el.addClass('btn-' + outline + str);        
1451     }
1452     
1453     
1454 });
1455 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1456
1457 Roo.bootstrap.Button.weights = [
1458     'default',
1459     'secondary' ,
1460     'primary',
1461     'success',
1462     'info',
1463     'warning',
1464     'danger',
1465     'link',
1466     'light',
1467     'dark'              
1468    
1469 ];/*
1470  * - LGPL
1471  *
1472  * column
1473  * 
1474  */
1475
1476 /**
1477  * @class Roo.bootstrap.Column
1478  * @extends Roo.bootstrap.Component
1479  * @children Roo.bootstrap.Component
1480  * Bootstrap Column class
1481  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1482  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1483  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1484  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1485  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1486  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1487  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1488  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1489  *
1490  * 
1491  * @cfg {Boolean} hidden (true|false) hide the element
1492  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1493  * @cfg {String} fa (ban|check|...) font awesome icon
1494  * @cfg {Number} fasize (1|2|....) font awsome size
1495
1496  * @cfg {String} icon (info-sign|check|...) glyphicon name
1497
1498  * @cfg {String} html content of column.
1499  * 
1500  * @constructor
1501  * Create a new Column
1502  * @param {Object} config The config object
1503  */
1504
1505 Roo.bootstrap.Column = function(config){
1506     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1507 };
1508
1509 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1510     
1511     xs: false,
1512     sm: false,
1513     md: false,
1514     lg: false,
1515     xsoff: false,
1516     smoff: false,
1517     mdoff: false,
1518     lgoff: false,
1519     html: '',
1520     offset: 0,
1521     alert: false,
1522     fa: false,
1523     icon : false,
1524     hidden : false,
1525     fasize : 1,
1526     
1527     getAutoCreate : function(){
1528         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1529         
1530         cfg = {
1531             tag: 'div',
1532             cls: 'column'
1533         };
1534         
1535         var settings=this;
1536         var sizes =   ['xs','sm','md','lg'];
1537         sizes.map(function(size ,ix){
1538             //Roo.log( size + ':' + settings[size]);
1539             
1540             if (settings[size+'off'] !== false) {
1541                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1542             }
1543             
1544             if (settings[size] === false) {
1545                 return;
1546             }
1547             
1548             if (!settings[size]) { // 0 = hidden
1549                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1550                 // bootsrap4
1551                 for (var i = ix; i > -1; i--) {
1552                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1553                 }
1554                 
1555                 
1556                 return;
1557             }
1558             cfg.cls += ' col-' + size + '-' + settings[size] + (
1559                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1560             );
1561             
1562         });
1563         
1564         if (this.hidden) {
1565             cfg.cls += ' hidden';
1566         }
1567         
1568         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1569             cfg.cls +=' alert alert-' + this.alert;
1570         }
1571         
1572         
1573         if (this.html.length) {
1574             cfg.html = this.html;
1575         }
1576         if (this.fa) {
1577             var fasize = '';
1578             if (this.fasize > 1) {
1579                 fasize = ' fa-' + this.fasize + 'x';
1580             }
1581             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1582             
1583             
1584         }
1585         if (this.icon) {
1586             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1587         }
1588         
1589         return cfg;
1590     }
1591    
1592 });
1593
1594  
1595
1596  /*
1597  * - LGPL
1598  *
1599  * page container.
1600  * 
1601  */
1602
1603
1604 /**
1605  * @class Roo.bootstrap.Container
1606  * @extends Roo.bootstrap.Component
1607  * @builder-top
1608  * @children Roo.bootstrap.Component
1609  * Bootstrap Container class
1610  * @cfg {Boolean} jumbotron is it a jumbotron element
1611  * @cfg {String} html content of element
1612  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1613  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1614  * @cfg {String} header content of header (for panel)
1615  * @cfg {String} footer content of footer (for panel)
1616  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1617  * @cfg {String} tag (header|aside|section) type of HTML tag.
1618  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1619  * @cfg {String} fa font awesome icon
1620  * @cfg {String} icon (info-sign|check|...) glyphicon name
1621  * @cfg {Boolean} hidden (true|false) hide the element
1622  * @cfg {Boolean} expandable (true|false) default false
1623  * @cfg {Boolean} expanded (true|false) default true
1624  * @cfg {String} rheader contet on the right of header
1625  * @cfg {Boolean} clickable (true|false) default false
1626
1627  *     
1628  * @constructor
1629  * Create a new Container
1630  * @param {Object} config The config object
1631  */
1632
1633 Roo.bootstrap.Container = function(config){
1634     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1635     
1636     this.addEvents({
1637         // raw events
1638          /**
1639          * @event expand
1640          * After the panel has been expand
1641          * 
1642          * @param {Roo.bootstrap.Container} this
1643          */
1644         "expand" : true,
1645         /**
1646          * @event collapse
1647          * After the panel has been collapsed
1648          * 
1649          * @param {Roo.bootstrap.Container} this
1650          */
1651         "collapse" : true,
1652         /**
1653          * @event click
1654          * When a element is chick
1655          * @param {Roo.bootstrap.Container} this
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1663     
1664     jumbotron : false,
1665     well: '',
1666     panel : '',
1667     header: '',
1668     footer : '',
1669     sticky: '',
1670     tag : false,
1671     alert : false,
1672     fa: false,
1673     icon : false,
1674     expandable : false,
1675     rheader : '',
1676     expanded : true,
1677     clickable: false,
1678   
1679      
1680     getChildContainer : function() {
1681         
1682         if(!this.el){
1683             return false;
1684         }
1685         
1686         if (this.panel.length) {
1687             return this.el.select('.panel-body',true).first();
1688         }
1689         
1690         return this.el;
1691     },
1692     
1693     
1694     getAutoCreate : function(){
1695         
1696         var cfg = {
1697             tag : this.tag || 'div',
1698             html : '',
1699             cls : ''
1700         };
1701         if (this.jumbotron) {
1702             cfg.cls = 'jumbotron';
1703         }
1704         
1705         
1706         
1707         // - this is applied by the parent..
1708         //if (this.cls) {
1709         //    cfg.cls = this.cls + '';
1710         //}
1711         
1712         if (this.sticky.length) {
1713             
1714             var bd = Roo.get(document.body);
1715             if (!bd.hasClass('bootstrap-sticky')) {
1716                 bd.addClass('bootstrap-sticky');
1717                 Roo.select('html',true).setStyle('height', '100%');
1718             }
1719              
1720             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1721         }
1722         
1723         
1724         if (this.well.length) {
1725             switch (this.well) {
1726                 case 'lg':
1727                 case 'sm':
1728                     cfg.cls +=' well well-' +this.well;
1729                     break;
1730                 default:
1731                     cfg.cls +=' well';
1732                     break;
1733             }
1734         }
1735         
1736         if (this.hidden) {
1737             cfg.cls += ' hidden';
1738         }
1739         
1740         
1741         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1742             cfg.cls +=' alert alert-' + this.alert;
1743         }
1744         
1745         var body = cfg;
1746         
1747         if (this.panel.length) {
1748             cfg.cls += ' panel panel-' + this.panel;
1749             cfg.cn = [];
1750             if (this.header.length) {
1751                 
1752                 var h = [];
1753                 
1754                 if(this.expandable){
1755                     
1756                     cfg.cls = cfg.cls + ' expandable';
1757                     
1758                     h.push({
1759                         tag: 'i',
1760                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1761                     });
1762                     
1763                 }
1764                 
1765                 h.push(
1766                     {
1767                         tag: 'span',
1768                         cls : 'panel-title',
1769                         html : (this.expandable ? '&nbsp;' : '') + this.header
1770                     },
1771                     {
1772                         tag: 'span',
1773                         cls: 'panel-header-right',
1774                         html: this.rheader
1775                     }
1776                 );
1777                 
1778                 cfg.cn.push({
1779                     cls : 'panel-heading',
1780                     style : this.expandable ? 'cursor: pointer' : '',
1781                     cn : h
1782                 });
1783                 
1784             }
1785             
1786             body = false;
1787             cfg.cn.push({
1788                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1789                 html : this.html
1790             });
1791             
1792             
1793             if (this.footer.length) {
1794                 cfg.cn.push({
1795                     cls : 'panel-footer',
1796                     html : this.footer
1797                     
1798                 });
1799             }
1800             
1801         }
1802         
1803         if (body) {
1804             body.html = this.html || cfg.html;
1805             // prefix with the icons..
1806             if (this.fa) {
1807                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1808             }
1809             if (this.icon) {
1810                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1811             }
1812             
1813             
1814         }
1815         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1816             cfg.cls =  'container';
1817         }
1818         
1819         return cfg;
1820     },
1821     
1822     initEvents: function() 
1823     {
1824         if(this.expandable){
1825             var headerEl = this.headerEl();
1826         
1827             if(headerEl){
1828                 headerEl.on('click', this.onToggleClick, this);
1829             }
1830         }
1831         
1832         if(this.clickable){
1833             this.el.on('click', this.onClick, this);
1834         }
1835         
1836     },
1837     
1838     onToggleClick : function()
1839     {
1840         var headerEl = this.headerEl();
1841         
1842         if(!headerEl){
1843             return;
1844         }
1845         
1846         if(this.expanded){
1847             this.collapse();
1848             return;
1849         }
1850         
1851         this.expand();
1852     },
1853     
1854     expand : function()
1855     {
1856         if(this.fireEvent('expand', this)) {
1857             
1858             this.expanded = true;
1859             
1860             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1861             
1862             this.el.select('.panel-body',true).first().removeClass('hide');
1863             
1864             var toggleEl = this.toggleEl();
1865
1866             if(!toggleEl){
1867                 return;
1868             }
1869
1870             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1871         }
1872         
1873     },
1874     
1875     collapse : function()
1876     {
1877         if(this.fireEvent('collapse', this)) {
1878             
1879             this.expanded = false;
1880             
1881             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1882             this.el.select('.panel-body',true).first().addClass('hide');
1883         
1884             var toggleEl = this.toggleEl();
1885
1886             if(!toggleEl){
1887                 return;
1888             }
1889
1890             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1891         }
1892     },
1893     
1894     toggleEl : function()
1895     {
1896         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1897             return;
1898         }
1899         
1900         return this.el.select('.panel-heading .fa',true).first();
1901     },
1902     
1903     headerEl : function()
1904     {
1905         if(!this.el || !this.panel.length || !this.header.length){
1906             return;
1907         }
1908         
1909         return this.el.select('.panel-heading',true).first()
1910     },
1911     
1912     bodyEl : function()
1913     {
1914         if(!this.el || !this.panel.length){
1915             return;
1916         }
1917         
1918         return this.el.select('.panel-body',true).first()
1919     },
1920     
1921     titleEl : function()
1922     {
1923         if(!this.el || !this.panel.length || !this.header.length){
1924             return;
1925         }
1926         
1927         return this.el.select('.panel-title',true).first();
1928     },
1929     
1930     setTitle : function(v)
1931     {
1932         var titleEl = this.titleEl();
1933         
1934         if(!titleEl){
1935             return;
1936         }
1937         
1938         titleEl.dom.innerHTML = v;
1939     },
1940     
1941     getTitle : function()
1942     {
1943         
1944         var titleEl = this.titleEl();
1945         
1946         if(!titleEl){
1947             return '';
1948         }
1949         
1950         return titleEl.dom.innerHTML;
1951     },
1952     
1953     setRightTitle : function(v)
1954     {
1955         var t = this.el.select('.panel-header-right',true).first();
1956         
1957         if(!t){
1958             return;
1959         }
1960         
1961         t.dom.innerHTML = v;
1962     },
1963     
1964     onClick : function(e)
1965     {
1966         e.preventDefault();
1967         
1968         this.fireEvent('click', this, e);
1969     }
1970 });
1971
1972  /**
1973  * @class Roo.bootstrap.Card
1974  * @extends Roo.bootstrap.Component
1975  * @children Roo.bootstrap.Component
1976  * @licence LGPL
1977  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1978  *
1979  *
1980  * possible... may not be implemented..
1981  * @cfg {String} header_image  src url of image.
1982  * @cfg {String|Object} header
1983  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1985  * 
1986  * @cfg {String} title
1987  * @cfg {String} subtitle
1988  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989  * @cfg {String} footer
1990  
1991  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1992  * 
1993  * @cfg {String} margin (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2000  *
2001  * @cfg {String} padding (0|1|2|3|4|5)
2002  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004  * @cfg {String} padding_left (0|1|2|3|4|5)
2005  * @cfg {String} padding_right (0|1|2|3|4|5)
2006  * @cfg {String} padding_x (0|1|2|3|4|5)
2007  * @cfg {String} padding_y (0|1|2|3|4|5)
2008  *
2009  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  
2015  * @config {Boolean} dragable  if this card can be dragged.
2016  * @config {String} drag_group  group for drag
2017  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2018  * @config {String} drop_group  group for drag
2019  * 
2020  * @config {Boolean} collapsable can the body be collapsed.
2021  * @config {Boolean} collapsed is the body collapsed when rendered...
2022  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023  * @config {Boolean} rotated is the body rotated when rendered...
2024  * 
2025  * @constructor
2026  * Create a new Container
2027  * @param {Object} config The config object
2028  */
2029
2030 Roo.bootstrap.Card = function(config){
2031     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2032     
2033     this.addEvents({
2034          // raw events
2035         /**
2036          * @event drop
2037          * When a element a card is dropped
2038          * @param {Roo.bootstrap.Card} this
2039          *
2040          * 
2041          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042          * @param {String} position 'above' or 'below'
2043          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2044         
2045          */
2046         'drop' : true,
2047          /**
2048          * @event rotate
2049          * When a element a card is rotate
2050          * @param {Roo.bootstrap.Card} this
2051          * @param {Roo.Element} n the node being dropped?
2052          * @param {Boolean} rotate status
2053          */
2054         'rotate' : true,
2055         /**
2056          * @event cardover
2057          * When a card element is dragged over ready to drop (return false to block dropable)
2058          * @param {Roo.bootstrap.Card} this
2059          * @param {Object} data from dragdrop 
2060          */
2061          'cardover' : true
2062          
2063     });
2064 };
2065
2066
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2068     
2069     
2070     weight : '',
2071     
2072     margin: '', /// may be better in component?
2073     margin_top: '', 
2074     margin_bottom: '', 
2075     margin_left: '',
2076     margin_right: '',
2077     margin_x: '',
2078     margin_y: '',
2079     
2080     padding : '',
2081     padding_top: '', 
2082     padding_bottom: '', 
2083     padding_left: '',
2084     padding_right: '',
2085     padding_x: '',
2086     padding_y: '',
2087     
2088     display: '', 
2089     display_xs: '', 
2090     display_sm: '', 
2091     display_lg: '',
2092     display_xl: '',
2093  
2094     header_image  : '',
2095     header : '',
2096     header_size : 0,
2097     title : '',
2098     subtitle : '',
2099     html : '',
2100     footer: '',
2101
2102     collapsable : false,
2103     collapsed : false,
2104     rotateable : false,
2105     rotated : false,
2106     
2107     dragable : false,
2108     drag_group : false,
2109     dropable : false,
2110     drop_group : false,
2111     childContainer : false,
2112     dropEl : false, /// the dom placeholde element that indicates drop location.
2113     containerEl: false, // body container
2114     bodyEl: false, // card-body
2115     headerContainerEl : false, //
2116     headerEl : false,
2117     header_imageEl : false,
2118     
2119     
2120     layoutCls : function()
2121     {
2122         var cls = '';
2123         var t = this;
2124         Roo.log(this.margin_bottom.length);
2125         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2127             
2128             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2130             }
2131             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2133             }
2134         });
2135         
2136         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2139             }
2140         });
2141         
2142         // more generic support?
2143         if (this.hidden) {
2144             cls += ' d-none';
2145         }
2146         
2147         return cls;
2148     },
2149  
2150        // Roo.log("Call onRender: " + this.xtype);
2151         /*  We are looking at something like this.
2152 <div class="card">
2153     <img src="..." class="card-img-top" alt="...">
2154     <div class="card-body">
2155         <h5 class="card-title">Card title</h5>
2156          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2157
2158         >> this bit is really the body...
2159         <div> << we will ad dthis in hopefully it will not break shit.
2160         
2161         ** card text does not actually have any styling...
2162         
2163             <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>
2164         
2165         </div> <<
2166           <a href="#" class="card-link">Card link</a>
2167           
2168     </div>
2169     <div class="card-footer">
2170         <small class="text-muted">Last updated 3 mins ago</small>
2171     </div>
2172 </div>
2173          */
2174     getAutoCreate : function(){
2175         
2176         var cfg = {
2177             tag : 'div',
2178             cls : 'card',
2179             cn : [ ]
2180         };
2181         
2182         if (this.weight.length && this.weight != 'light') {
2183             cfg.cls += ' text-white';
2184         } else {
2185             cfg.cls += ' text-dark'; // need as it's nested..
2186         }
2187         if (this.weight.length) {
2188             cfg.cls += ' bg-' + this.weight;
2189         }
2190         
2191         cfg.cls += ' ' + this.layoutCls(); 
2192         
2193         var hdr = false;
2194         var hdr_ctr = false;
2195         if (this.header.length) {
2196             hdr = {
2197                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2199                 cn : []
2200             };
2201             cfg.cn.push(hdr);
2202             hdr_ctr = hdr;
2203         } else {
2204             hdr = {
2205                 tag : 'div',
2206                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2207                 cn : []
2208             };
2209             cfg.cn.push(hdr);
2210             hdr_ctr = hdr;
2211         }
2212         if (this.collapsable) {
2213             hdr_ctr = {
2214             tag : 'a',
2215             cls : 'd-block user-select-none',
2216             cn: [
2217                     {
2218                         tag: 'i',
2219                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2220                     }
2221                    
2222                 ]
2223             };
2224             hdr.cn.push(hdr_ctr);
2225         }
2226         
2227         hdr_ctr.cn.push(        {
2228             tag: 'span',
2229             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2230             html : this.header
2231         });
2232         
2233         
2234         if (this.header_image.length) {
2235             cfg.cn.push({
2236                 tag : 'img',
2237                 cls : 'card-img-top',
2238                 src: this.header_image // escape?
2239             });
2240         } else {
2241             cfg.cn.push({
2242                     tag : 'div',
2243                     cls : 'card-img-top d-none' 
2244                 });
2245         }
2246             
2247         var body = {
2248             tag : 'div',
2249             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2250             cn : []
2251         };
2252         var obody = body;
2253         if (this.collapsable || this.rotateable) {
2254             obody = {
2255                 tag: 'div',
2256                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2257                 cn : [  body ]
2258             };
2259         }
2260         
2261         cfg.cn.push(obody);
2262         
2263         if (this.title.length) {
2264             body.cn.push({
2265                 tag : 'div',
2266                 cls : 'card-title',
2267                 src: this.title // escape?
2268             });
2269         }  
2270         
2271         if (this.subtitle.length) {
2272             body.cn.push({
2273                 tag : 'div',
2274                 cls : 'card-title',
2275                 src: this.subtitle // escape?
2276             });
2277         }
2278         
2279         body.cn.push({
2280             tag : 'div',
2281             cls : 'roo-card-body-ctr'
2282         });
2283         
2284         if (this.html.length) {
2285             body.cn.push({
2286                 tag: 'div',
2287                 html : this.html
2288             });
2289         }
2290         // fixme ? handle objects?
2291         
2292         if (this.footer.length) {
2293            
2294             cfg.cn.push({
2295                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2296                 html : this.footer
2297             });
2298             
2299         } else {
2300             cfg.cn.push({cls : 'card-footer d-none'});
2301         }
2302         
2303         // footer...
2304         
2305         return cfg;
2306     },
2307     
2308     
2309     getCardHeader : function()
2310     {
2311         var  ret = this.el.select('.card-header',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardFooter : function()
2319     {
2320         var  ret = this.el.select('.card-footer',true).first();
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324         
2325         return ret;
2326     },
2327     getCardImageTop : function()
2328     {
2329         var  ret = this.header_imageEl;
2330         if (ret.hasClass('d-none')) {
2331             ret.removeClass('d-none');
2332         }
2333             
2334         return ret;
2335     },
2336     
2337     getChildContainer : function()
2338     {
2339         
2340         if(!this.el){
2341             return false;
2342         }
2343         return this.el.select('.roo-card-body-ctr',true).first();    
2344     },
2345     
2346     initEvents: function() 
2347     {
2348         this.bodyEl = this.el.select('.card-body',true).first(); 
2349         this.containerEl = this.getChildContainer();
2350         if(this.dragable){
2351             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352                     containerScroll: true,
2353                     ddGroup: this.drag_group || 'default_card_drag_group'
2354             });
2355             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2356         }
2357         if (this.dropable) {
2358             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359                 containerScroll: true,
2360                 ddGroup: this.drop_group || 'default_card_drag_group'
2361             });
2362             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2367         }
2368         
2369         if (this.collapsable) {
2370             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2371         }
2372         if (this.rotateable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2374         }
2375         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2376          
2377         this.footerEl = this.el.select('.card-footer',true).first();
2378         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380         this.headerEl = this.el.select('.card-header',true).first();
2381         
2382         if (this.rotated) {
2383             this.el.addClass('roo-card-rotated');
2384             this.fireEvent('rotate', this, true);
2385         }
2386         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2387         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2388         
2389     },
2390     getDragData : function(e)
2391     {
2392         var target = this.getEl();
2393         if (target) {
2394             //this.handleSelection(e);
2395             
2396             var dragData = {
2397                 source: this,
2398                 copy: false,
2399                 nodes: this.getEl(),
2400                 records: []
2401             };
2402             
2403             
2404             dragData.ddel = target.dom ;    // the div element
2405             Roo.log(target.getWidth( ));
2406             dragData.ddel.style.width = target.getWidth() + 'px';
2407             
2408             return dragData;
2409         }
2410         return false;
2411     },
2412     /**
2413     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2414     *    whole Element becomes the target, and this causes the drop gesture to append.
2415     *
2416     *    Returns an object:
2417     *     {
2418            
2419            position : 'below' or 'above'
2420            card  : relateive to card OBJECT (or true for no cards listed)
2421            items_n : relative to nth item in list
2422            card_n : relative to  nth card in list
2423     }
2424     *
2425     *    
2426     */
2427     getTargetFromEvent : function(e, dragged_card_el)
2428     {
2429         var target = e.getTarget();
2430         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431             target = target.parentNode;
2432         }
2433         
2434         var ret = {
2435             position: '',
2436             cards : [],
2437             card_n : -1,
2438             items_n : -1,
2439             card : false 
2440         };
2441         
2442         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443         // see if target is one of the 'cards'...
2444         
2445         
2446         //Roo.log(this.items.length);
2447         var pos = false;
2448         
2449         var last_card_n = 0;
2450         var cards_len  = 0;
2451         for (var i = 0;i< this.items.length;i++) {
2452             
2453             if (!this.items[i].el.hasClass('card')) {
2454                  continue;
2455             }
2456             pos = this.getDropPoint(e, this.items[i].el.dom);
2457             
2458             cards_len = ret.cards.length;
2459             //Roo.log(this.items[i].el.dom.id);
2460             ret.cards.push(this.items[i]);
2461             last_card_n  = i;
2462             if (ret.card_n < 0 && pos == 'above') {
2463                 ret.position = cards_len > 0 ? 'below' : pos;
2464                 ret.items_n = i > 0 ? i - 1 : 0;
2465                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2466                 ret.card = ret.cards[ret.card_n];
2467             }
2468         }
2469         if (!ret.cards.length) {
2470             ret.card = true;
2471             ret.position = 'below';
2472             ret.items_n;
2473             return ret;
2474         }
2475         // could not find a card.. stick it at the end..
2476         if (ret.card_n < 0) {
2477             ret.card_n = last_card_n;
2478             ret.card = ret.cards[last_card_n];
2479             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480             ret.position = 'below';
2481         }
2482         
2483         if (this.items[ret.items_n].el == dragged_card_el) {
2484             return false;
2485         }
2486         
2487         if (ret.position == 'below') {
2488             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2489             
2490             if (card_after  && card_after.el == dragged_card_el) {
2491                 return false;
2492             }
2493             return ret;
2494         }
2495         
2496         // its's after ..
2497         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2498         
2499         if (card_before  && card_before.el == dragged_card_el) {
2500             return false;
2501         }
2502         
2503         return ret;
2504     },
2505     
2506     onNodeEnter : function(n, dd, e, data){
2507         return false;
2508     },
2509     onNodeOver : function(n, dd, e, data)
2510     {
2511        
2512         var target_info = this.getTargetFromEvent(e,data.source.el);
2513         if (target_info === false) {
2514             this.dropPlaceHolder('hide');
2515             return false;
2516         }
2517         Roo.log(['getTargetFromEvent', target_info ]);
2518         
2519         
2520         if (this.fireEvent('cardover', this, [ data ]) === false) {
2521             return false;
2522         }
2523         
2524         this.dropPlaceHolder('show', target_info,data);
2525         
2526         return false; 
2527     },
2528     onNodeOut : function(n, dd, e, data){
2529         this.dropPlaceHolder('hide');
2530      
2531     },
2532     onNodeDrop : function(n, dd, e, data)
2533     {
2534         
2535         // call drop - return false if
2536         
2537         // this could actually fail - if the Network drops..
2538         // we will ignore this at present..- client should probably reload
2539         // the whole set of cards if stuff like that fails.
2540         
2541         
2542         var info = this.getTargetFromEvent(e,data.source.el);
2543         if (info === false) {
2544             return false;
2545         }
2546         this.dropPlaceHolder('hide');
2547   
2548           
2549     
2550         this.acceptCard(data.source, info.position, info.card, info.items_n);
2551         return true;
2552          
2553     },
2554     firstChildCard : function()
2555     {
2556         for (var i = 0;i< this.items.length;i++) {
2557             
2558             if (!this.items[i].el.hasClass('card')) {
2559                  continue;
2560             }
2561             return this.items[i];
2562         }
2563         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2564     },
2565     /**
2566      * accept card
2567      *
2568      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2569      */
2570     acceptCard : function(move_card,  position, next_to_card )
2571     {
2572         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2573             return false;
2574         }
2575         
2576         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2577         
2578         move_card.parent().removeCard(move_card);
2579         
2580         
2581         var dom = move_card.el.dom;
2582         dom.style.width = ''; // clear with - which is set by drag.
2583         
2584         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585             var cardel = next_to_card.el.dom;
2586             
2587             if (position == 'above' ) {
2588                 cardel.parentNode.insertBefore(dom, cardel);
2589             } else if (cardel.nextSibling) {
2590                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2591             } else {
2592                 cardel.parentNode.append(dom);
2593             }
2594         } else {
2595             // card container???
2596             this.containerEl.dom.append(dom);
2597         }
2598         
2599         //FIXME HANDLE card = true 
2600         
2601         // add this to the correct place in items.
2602         
2603         // remove Card from items.
2604         
2605        
2606         if (this.items.length) {
2607             var nitems = [];
2608             //Roo.log([info.items_n, info.position, this.items.length]);
2609             for (var i =0; i < this.items.length; i++) {
2610                 if (i == to_items_n && position == 'above') {
2611                     nitems.push(move_card);
2612                 }
2613                 nitems.push(this.items[i]);
2614                 if (i == to_items_n && position == 'below') {
2615                     nitems.push(move_card);
2616                 }
2617             }
2618             this.items = nitems;
2619             Roo.log(this.items);
2620         } else {
2621             this.items.push(move_card);
2622         }
2623         
2624         move_card.parentId = this.id;
2625         
2626         return true;
2627         
2628         
2629     },
2630     removeCard : function(c)
2631     {
2632         this.items = this.items.filter(function(e) { return e != c });
2633  
2634         var dom = c.el.dom;
2635         dom.parentNode.removeChild(dom);
2636         dom.style.width = ''; // clear with - which is set by drag.
2637         c.parentId = false;
2638         
2639     },
2640     
2641     /**    Decide whether to drop above or below a View node. */
2642     getDropPoint : function(e, n, dd)
2643     {
2644         if (dd) {
2645              return false;
2646         }
2647         if (n == this.containerEl.dom) {
2648             return "above";
2649         }
2650         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651         var c = t + (b - t) / 2;
2652         var y = Roo.lib.Event.getPageY(e);
2653         if(y <= c) {
2654             return "above";
2655         }else{
2656             return "below";
2657         }
2658     },
2659     onToggleCollapse : function(e)
2660         {
2661         if (this.collapsed) {
2662             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663             this.collapsableEl.addClass('show');
2664             this.collapsed = false;
2665             return;
2666         }
2667         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668         this.collapsableEl.removeClass('show');
2669         this.collapsed = true;
2670         
2671     
2672     },
2673     
2674     onToggleRotate : function(e)
2675     {
2676         this.collapsableEl.removeClass('show');
2677         this.footerEl.removeClass('d-none');
2678         this.el.removeClass('roo-card-rotated');
2679         this.el.removeClass('d-none');
2680         if (this.rotated) {
2681             
2682             this.collapsableEl.addClass('show');
2683             this.rotated = false;
2684             this.fireEvent('rotate', this, this.rotated);
2685             return;
2686         }
2687         this.el.addClass('roo-card-rotated');
2688         this.footerEl.addClass('d-none');
2689         this.el.select('.roo-collapsable').removeClass('show');
2690         
2691         this.rotated = true;
2692         this.fireEvent('rotate', this, this.rotated);
2693     
2694     },
2695     
2696     dropPlaceHolder: function (action, info, data)
2697     {
2698         if (this.dropEl === false) {
2699             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2700             cls : 'd-none'
2701             },true);
2702         }
2703         this.dropEl.removeClass(['d-none', 'd-block']);        
2704         if (action == 'hide') {
2705             
2706             this.dropEl.addClass('d-none');
2707             return;
2708         }
2709         // FIXME - info.card == true!!!
2710         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2711         
2712         if (info.card !== true) {
2713             var cardel = info.card.el.dom;
2714             
2715             if (info.position == 'above') {
2716                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717             } else if (cardel.nextSibling) {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2719             } else {
2720                 cardel.parentNode.append(this.dropEl.dom);
2721             }
2722         } else {
2723             // card container???
2724             this.containerEl.dom.append(this.dropEl.dom);
2725         }
2726         
2727         this.dropEl.addClass('d-block roo-card-dropzone');
2728         
2729         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2730         
2731         
2732     
2733     
2734     
2735     },
2736     setHeaderText: function(html)
2737     {
2738         this.header = html;
2739         if (this.headerContainerEl) {
2740             this.headerContainerEl.dom.innerHTML = html;
2741         }
2742     },
2743     onHeaderImageLoad : function(ev, he)
2744     {
2745         if (!this.header_image_fit_square) {
2746             return;
2747         }
2748         
2749         var hw = he.naturalHeight / he.naturalWidth;
2750         // wide image = < 0
2751         // tall image = > 1
2752         //var w = he.dom.naturalWidth;
2753         var ww = he.width;
2754         he.style.left =  0;
2755         he.style.position =  'relative';
2756         if (hw > 1) {
2757             var nw = (ww * (1/hw));
2758             Roo.get(he).setSize( ww * (1/hw),  ww);
2759             he.style.left =  ((ww - nw)/ 2) + 'px';
2760             he.style.position =  'relative';
2761         }
2762
2763     }
2764
2765     
2766 });
2767
2768 /*
2769  * - LGPL
2770  *
2771  * Card header - holder for the card header elements.
2772  * 
2773  */
2774
2775 /**
2776  * @class Roo.bootstrap.CardHeader
2777  * @extends Roo.bootstrap.Element
2778  * @parent Roo.bootstrap.Card
2779  * @children Roo.bootstrap.Component
2780  * Bootstrap CardHeader class
2781  * @constructor
2782  * Create a new Card Header - that you can embed children into
2783  * @param {Object} config The config object
2784  */
2785
2786 Roo.bootstrap.CardHeader = function(config){
2787     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2788 };
2789
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2791     
2792     
2793     container_method : 'getCardHeader' 
2794     
2795      
2796     
2797     
2798    
2799 });
2800
2801  
2802
2803  /*
2804  * - LGPL
2805  *
2806  * Card footer - holder for the card footer elements.
2807  * 
2808  */
2809
2810 /**
2811  * @class Roo.bootstrap.CardFooter
2812  * @extends Roo.bootstrap.Element
2813  * @parent Roo.bootstrap.Card
2814  * @children Roo.bootstrap.Component
2815  * Bootstrap CardFooter class
2816  * 
2817  * @constructor
2818  * Create a new Card Footer - that you can embed children into
2819  * @param {Object} config The config object
2820  */
2821
2822 Roo.bootstrap.CardFooter = function(config){
2823     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2824 };
2825
2826 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2827     
2828     
2829     container_method : 'getCardFooter' 
2830     
2831      
2832     
2833     
2834    
2835 });
2836
2837  
2838
2839  /*
2840  * - LGPL
2841  *
2842  * Card header - holder for the card header elements.
2843  * 
2844  */
2845
2846 /**
2847  * @class Roo.bootstrap.CardImageTop
2848  * @extends Roo.bootstrap.Element
2849  * @parent Roo.bootstrap.Card
2850  * @children Roo.bootstrap.Component
2851  * Bootstrap CardImageTop class
2852  * 
2853  * @constructor
2854  * Create a new Card Image Top container
2855  * @param {Object} config The config object
2856  */
2857
2858 Roo.bootstrap.CardImageTop = function(config){
2859     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2860 };
2861
2862 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2863     
2864    
2865     container_method : 'getCardImageTop' 
2866     
2867      
2868     
2869    
2870 });
2871
2872  
2873
2874  
2875 /*
2876 * Licence: LGPL
2877 */
2878
2879 /**
2880  * @class Roo.bootstrap.ButtonUploader
2881  * @extends Roo.bootstrap.Button
2882  * Bootstrap Button Uploader class - it's a button which when you add files to it
2883  *
2884  * 
2885  * @cfg {Number} errorTimeout default 3000
2886  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2887  * @cfg {Array}  html The button text.
2888  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2889  *
2890  * @constructor
2891  * Create a new CardUploader
2892  * @param {Object} config The config object
2893  */
2894
2895 Roo.bootstrap.ButtonUploader = function(config){
2896     
2897  
2898     
2899     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2900     
2901      
2902      this.addEvents({
2903          // raw events
2904         /**
2905          * @event beforeselect
2906          * When button is pressed, before show upload files dialog is shown
2907          * @param {Roo.bootstrap.UploaderButton} this
2908          *
2909          */
2910         'beforeselect' : true,
2911          /**
2912          * @event fired when files have been selected, 
2913          * When a the download link is clicked
2914          * @param {Roo.bootstrap.UploaderButton} this
2915          * @param {Array} Array of files that have been uploaded
2916          */
2917         'uploaded' : true
2918         
2919     });
2920 };
2921  
2922 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2923     
2924      
2925     errorTimeout : 3000,
2926      
2927     images : false,
2928    
2929     fileCollection : false,
2930     allowBlank : true,
2931     
2932     multiple : true,
2933     
2934     getAutoCreate : function()
2935     {
2936         var im = {
2937             tag: 'input',
2938             type : 'file',
2939             cls : 'd-none  roo-card-upload-selector' 
2940           
2941         };
2942         if (this.multiple) {
2943             im.multiple = 'multiple';
2944         }
2945         
2946         return  {
2947             cls :'div' ,
2948             cn : [
2949                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2950                 im
2951
2952             ]
2953         };
2954            
2955          
2956     },
2957      
2958    
2959     initEvents : function()
2960     {
2961         
2962         Roo.bootstrap.Button.prototype.initEvents.call(this);
2963         
2964         
2965         
2966         
2967         
2968         this.urlAPI = (window.createObjectURL && window) || 
2969                                 (window.URL && URL.revokeObjectURL && URL) || 
2970                                 (window.webkitURL && webkitURL);
2971                         
2972          
2973          
2974          
2975         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  Roo.bootstrap.menu = Roo.bootstrap.menu || {};
3425 /**
3426  * @class Roo.bootstrap.MenuMgr
3427  * @licence LGPL
3428  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3429  * @singleton
3430  */
3431 Roo.bootstrap.menu.Manager = function(){
3432    var menus, active, groups = {}, attached = false, lastShow = new Date();
3433
3434    // private - called when first menu is created
3435    function init(){
3436        menus = {};
3437        active = new Roo.util.MixedCollection();
3438        Roo.get(document).addKeyListener(27, function(){
3439            if(active.length > 0){
3440                hideAll();
3441            }
3442        });
3443    }
3444
3445    // private
3446    function hideAll(){
3447        if(active && active.length > 0){
3448            var c = active.clone();
3449            c.each(function(m){
3450                m.hide();
3451            });
3452        }
3453    }
3454
3455    // private
3456    function onHide(m){
3457        active.remove(m);
3458        if(active.length < 1){
3459            Roo.get(document).un("mouseup", onMouseDown);
3460             
3461            attached = false;
3462        }
3463    }
3464
3465    // private
3466    function onShow(m){
3467        var last = active.last();
3468        lastShow = new Date();
3469        active.add(m);
3470        if(!attached){
3471           Roo.get(document).on("mouseup", onMouseDown);
3472            
3473            attached = true;
3474        }
3475        if(m.parentMenu){
3476           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3477           m.parentMenu.activeChild = m;
3478        }else if(last && last.isVisible()){
3479           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3480        }
3481    }
3482
3483    // private
3484    function onBeforeHide(m){
3485        if(m.activeChild){
3486            m.activeChild.hide();
3487        }
3488        if(m.autoHideTimer){
3489            clearTimeout(m.autoHideTimer);
3490            delete m.autoHideTimer;
3491        }
3492    }
3493
3494    // private
3495    function onBeforeShow(m){
3496        var pm = m.parentMenu;
3497        if(!pm && !m.allowOtherMenus){
3498            hideAll();
3499        }else if(pm && pm.activeChild && active != m){
3500            pm.activeChild.hide();
3501        }
3502    }
3503
3504    // private this should really trigger on mouseup..
3505    function onMouseDown(e){
3506         Roo.log("on Mouse Up");
3507         
3508         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3509             Roo.log("MenuManager hideAll");
3510             hideAll();
3511             e.stopEvent();
3512         }
3513         
3514         
3515    }
3516
3517    // private
3518    function onBeforeCheck(mi, state){
3519        if(state){
3520            var g = groups[mi.group];
3521            for(var i = 0, l = g.length; i < l; i++){
3522                if(g[i] != mi){
3523                    g[i].setChecked(false);
3524                }
3525            }
3526        }
3527    }
3528
3529    return {
3530
3531        /**
3532         * Hides all menus that are currently visible
3533         */
3534        hideAll : function(){
3535             hideAll();  
3536        },
3537
3538        // private
3539        register : function(menu){
3540            if(!menus){
3541                init();
3542            }
3543            menus[menu.id] = menu;
3544            menu.on("beforehide", onBeforeHide);
3545            menu.on("hide", onHide);
3546            menu.on("beforeshow", onBeforeShow);
3547            menu.on("show", onShow);
3548            var g = menu.group;
3549            if(g && menu.events["checkchange"]){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menu);
3554                menu.on("checkchange", onCheck);
3555            }
3556        },
3557
3558         /**
3559          * Returns a {@link Roo.menu.Menu} object
3560          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3561          * be used to generate and return a new Menu instance.
3562          */
3563        get : function(menu){
3564            if(typeof menu == "string"){ // menu id
3565                return menus[menu];
3566            }else if(menu.events){  // menu instance
3567                return menu;
3568            }
3569            /*else if(typeof menu.length == 'number'){ // array of menu items?
3570                return new Roo.bootstrap.Menu({items:menu});
3571            }else{ // otherwise, must be a config
3572                return new Roo.bootstrap.Menu(menu);
3573            }
3574            */
3575            return false;
3576        },
3577
3578        // private
3579        unregister : function(menu){
3580            delete menus[menu.id];
3581            menu.un("beforehide", onBeforeHide);
3582            menu.un("hide", onHide);
3583            menu.un("beforeshow", onBeforeShow);
3584            menu.un("show", onShow);
3585            var g = menu.group;
3586            if(g && menu.events["checkchange"]){
3587                groups[g].remove(menu);
3588                menu.un("checkchange", onCheck);
3589            }
3590        },
3591
3592        // private
3593        registerCheckable : function(menuItem){
3594            var g = menuItem.group;
3595            if(g){
3596                if(!groups[g]){
3597                    groups[g] = [];
3598                }
3599                groups[g].push(menuItem);
3600                menuItem.on("beforecheckchange", onBeforeCheck);
3601            }
3602        },
3603
3604        // private
3605        unregisterCheckable : function(menuItem){
3606            var g = menuItem.group;
3607            if(g){
3608                groups[g].remove(menuItem);
3609                menuItem.un("beforecheckchange", onBeforeCheck);
3610            }
3611        }
3612    };
3613 }(); 
3614 /**
3615  * @class Roo.bootstrap.menu.Menu
3616  * @extends Roo.bootstrap.Component
3617  * @licence LGPL
3618  * @children Roo.bootstrap.menu.Item
3619  * Bootstrap Menu class - container for MenuItems
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @licence LGPL
4094  * Bootstrap MenuItem class
4095  * 
4096  * @cfg {String} html the menu label
4097  * @cfg {String} href the link
4098  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4099  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4100  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4101  * @cfg {String} fa favicon to show on left of menu item.
4102  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4103  * 
4104  * 
4105  * @constructor
4106  * Create a new MenuItem
4107  * @param {Object} config The config object
4108  */
4109
4110
4111 Roo.bootstrap.menu.Item = function(config){
4112     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4113     this.addEvents({
4114         // raw events
4115         /**
4116          * @event click
4117          * The raw click event for the entire grid.
4118          * @param {Roo.bootstrap.menu.Item} this
4119          * @param {Roo.EventObject} e
4120          */
4121         "click" : true
4122     });
4123 };
4124
4125 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4126     
4127     href : false,
4128     html : false,
4129     preventDefault: false,
4130     isContainer : false,
4131     active : false,
4132     fa: false,
4133     
4134     getAutoCreate : function(){
4135         
4136         if(this.isContainer){
4137             return {
4138                 tag: 'li',
4139                 cls: 'dropdown-menu-item '
4140             };
4141         }
4142         var ctag = {
4143             tag: 'span',
4144             html: 'Link'
4145         };
4146         
4147         var anc = {
4148             tag : 'a',
4149             cls : 'dropdown-item',
4150             href : '#',
4151             cn : [  ]
4152         };
4153         
4154         if (this.fa !== false) {
4155             anc.cn.push({
4156                 tag : 'i',
4157                 cls : 'fa fa-' + this.fa
4158             });
4159         }
4160         
4161         anc.cn.push(ctag);
4162         
4163         
4164         var cfg= {
4165             tag: 'li',
4166             cls: 'dropdown-menu-item',
4167             cn: [ anc ]
4168         };
4169         if (this.parent().type == 'treeview') {
4170             cfg.cls = 'treeview-menu';
4171         }
4172         if (this.active) {
4173             cfg.cls += ' active';
4174         }
4175         
4176         
4177         
4178         anc.href = this.href || cfg.cn[0].href ;
4179         ctag.html = this.html || cfg.cn[0].html ;
4180         return cfg;
4181     },
4182     
4183     initEvents: function()
4184     {
4185         if (this.parent().type == 'treeview') {
4186             this.el.select('a').on('click', this.onClick, this);
4187         }
4188         
4189         if (this.menu) {
4190             this.menu.parentType = this.xtype;
4191             this.menu.triggerEl = this.el;
4192             this.menu = this.addxtype(Roo.apply({}, this.menu));
4193         }
4194         
4195     },
4196     onClick : function(e)
4197     {
4198         Roo.log('item on click ');
4199         
4200         if(this.preventDefault){
4201             e.preventDefault();
4202         }
4203         //this.parent().hideMenuItems();
4204         
4205         this.fireEvent('click', this, e);
4206     },
4207     getEl : function()
4208     {
4209         return this.el;
4210     } 
4211 });
4212
4213  
4214
4215  
4216
4217   
4218 /**
4219  * @class Roo.bootstrap.menu.Separator
4220  * @extends Roo.bootstrap.Component
4221  * @licence LGPL
4222  * Bootstrap Separator class
4223  * 
4224  * @constructor
4225  * Create a new Separator
4226  * @param {Object} config The config object
4227  */
4228
4229
4230 Roo.bootstrap.menu.Separator = function(config){
4231     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4232 };
4233
4234 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4235     
4236     getAutoCreate : function(){
4237         var cfg = {
4238             tag : 'li',
4239             cls: 'dropdown-divider divider'
4240         };
4241         
4242         return cfg;
4243     }
4244    
4245 });
4246
4247  
4248
4249  // deprciated 
4250 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
4251 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
4252 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
4253
4254
4255 /*
4256 * Licence: LGPL
4257 */
4258
4259 /**
4260  * @class Roo.bootstrap.Modal
4261  * @extends Roo.bootstrap.Component
4262  * @builder-top
4263  * @parent none
4264  * @children Roo.bootstrap.Component
4265  * Bootstrap Modal class
4266  * @cfg {String} title Title of dialog
4267  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4269  * @cfg {Boolean} specificTitle default false
4270  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4271  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272  * @cfg {Boolean} animate default true
4273  * @cfg {Boolean} allow_close default true
4274  * @cfg {Boolean} fitwindow default false
4275  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278  * @cfg {String} size (sm|lg|xl) default empty
4279  * @cfg {Number} max_width set the max width of modal
4280  * @cfg {Boolean} editableTitle can the title be edited
4281
4282  *
4283  *
4284  * @constructor
4285  * Create a new Modal Dialog
4286  * @param {Object} config The config object
4287  */
4288
4289 Roo.bootstrap.Modal = function(config){
4290     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291     this.addEvents({
4292         // raw events
4293         /**
4294          * @event btnclick
4295          * The raw btnclick event for the button
4296          * @param {Roo.EventObject} e
4297          */
4298         "btnclick" : true,
4299         /**
4300          * @event resize
4301          * Fire when dialog resize
4302          * @param {Roo.bootstrap.Modal} this
4303          * @param {Roo.EventObject} e
4304          */
4305         "resize" : true,
4306         /**
4307          * @event titlechanged
4308          * Fire when the editable title has been changed
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} value
4311          */
4312         "titlechanged" : true 
4313         
4314     });
4315     this.buttons = this.buttons || [];
4316
4317     if (this.tmpl) {
4318         this.tmpl = Roo.factory(this.tmpl);
4319     }
4320
4321 };
4322
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4324
4325     title : 'test dialog',
4326
4327     buttons : false,
4328
4329     // set on load...
4330
4331     html: false,
4332
4333     tmp: false,
4334
4335     specificTitle: false,
4336
4337     buttonPosition: 'right',
4338
4339     allow_close : true,
4340
4341     animate : true,
4342
4343     fitwindow: false,
4344     
4345      // private
4346     dialogEl: false,
4347     bodyEl:  false,
4348     footerEl:  false,
4349     titleEl:  false,
4350     closeEl:  false,
4351
4352     size: '',
4353     
4354     max_width: 0,
4355     
4356     max_height: 0,
4357     
4358     fit_content: false,
4359     editableTitle  : false,
4360
4361     onRender : function(ct, position)
4362     {
4363         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364
4365         if(!this.el){
4366             var cfg = Roo.apply({},  this.getAutoCreate());
4367             cfg.id = Roo.id();
4368             //if(!cfg.name){
4369             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4370             //}
4371             //if (!cfg.name.length) {
4372             //    delete cfg.name;
4373            // }
4374             if (this.cls) {
4375                 cfg.cls += ' ' + this.cls;
4376             }
4377             if (this.style) {
4378                 cfg.style = this.style;
4379             }
4380             this.el = Roo.get(document.body).createChild(cfg, position);
4381         }
4382         //var type = this.el.dom.type;
4383
4384
4385         if(this.tabIndex !== undefined){
4386             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387         }
4388
4389         this.dialogEl = this.el.select('.modal-dialog',true).first();
4390         this.bodyEl = this.el.select('.modal-body',true).first();
4391         this.closeEl = this.el.select('.modal-header .close', true).first();
4392         this.headerEl = this.el.select('.modal-header',true).first();
4393         this.titleEl = this.el.select('.modal-title',true).first();
4394         this.footerEl = this.el.select('.modal-footer',true).first();
4395
4396         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4397         
4398         //this.el.addClass("x-dlg-modal");
4399
4400         if (this.buttons.length) {
4401             Roo.each(this.buttons, function(bb) {
4402                 var b = Roo.apply({}, bb);
4403                 b.xns = b.xns || Roo.bootstrap;
4404                 b.xtype = b.xtype || 'Button';
4405                 if (typeof(b.listeners) == 'undefined') {
4406                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4407                 }
4408
4409                 var btn = Roo.factory(b);
4410
4411                 btn.render(this.getButtonContainer());
4412
4413             },this);
4414         }
4415         // render the children.
4416         var nitems = [];
4417
4418         if(typeof(this.items) != 'undefined'){
4419             var items = this.items;
4420             delete this.items;
4421
4422             for(var i =0;i < items.length;i++) {
4423                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424             }
4425         }
4426
4427         this.items = nitems;
4428
4429         // where are these used - they used to be body/close/footer
4430
4431
4432         this.initEvents();
4433         //this.el.addClass([this.fieldClass, this.cls]);
4434
4435     },
4436
4437     getAutoCreate : function()
4438     {
4439         // we will default to modal-body-overflow - might need to remove or make optional later.
4440         var bdy = {
4441                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4442                 html : this.html || ''
4443         };
4444
4445         var title = {
4446             tag: 'h5',
4447             cls : 'modal-title',
4448             html : this.title
4449         };
4450
4451         if(this.specificTitle){ // WTF is this?
4452             title = this.title;
4453         }
4454
4455         var header = [];
4456         if (this.allow_close && Roo.bootstrap.version == 3) {
4457             header.push({
4458                 tag: 'button',
4459                 cls : 'close',
4460                 html : '&times'
4461             });
4462         }
4463
4464         header.push(title);
4465
4466         if (this.editableTitle) {
4467             header.push({
4468                 cls: 'form-control roo-editable-title d-none',
4469                 tag: 'input',
4470                 type: 'text'
4471             });
4472         }
4473         
4474         if (this.allow_close && Roo.bootstrap.version == 4) {
4475             header.push({
4476                 tag: 'button',
4477                 cls : 'close',
4478                 html : '&times'
4479             });
4480         }
4481         
4482         var size = '';
4483
4484         if(this.size.length){
4485             size = 'modal-' + this.size;
4486         }
4487         
4488         var footer = Roo.bootstrap.version == 3 ?
4489             {
4490                 cls : 'modal-footer',
4491                 cn : [
4492                     {
4493                         tag: 'div',
4494                         cls: 'btn-' + this.buttonPosition
4495                     }
4496                 ]
4497
4498             } :
4499             {  // BS4 uses mr-auto on left buttons....
4500                 cls : 'modal-footer'
4501             };
4502
4503             
4504
4505         
4506         
4507         var modal = {
4508             cls: "modal",
4509              cn : [
4510                 {
4511                     cls: "modal-dialog " + size,
4512                     cn : [
4513                         {
4514                             cls : "modal-content",
4515                             cn : [
4516                                 {
4517                                     cls : 'modal-header',
4518                                     cn : header
4519                                 },
4520                                 bdy,
4521                                 footer
4522                             ]
4523
4524                         }
4525                     ]
4526
4527                 }
4528             ]
4529         };
4530
4531         if(this.animate){
4532             modal.cls += ' fade';
4533         }
4534
4535         return modal;
4536
4537     },
4538     getChildContainer : function() {
4539
4540          return this.bodyEl;
4541
4542     },
4543     getButtonContainer : function() {
4544         
4545          return Roo.bootstrap.version == 4 ?
4546             this.el.select('.modal-footer',true).first()
4547             : this.el.select('.modal-footer div',true).first();
4548
4549     },
4550     initEvents : function()
4551     {
4552         if (this.allow_close) {
4553             this.closeEl.on('click', this.hide, this);
4554         }
4555         Roo.EventManager.onWindowResize(this.resize, this, true);
4556         if (this.editableTitle) {
4557             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4558             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559             this.headerEditEl.on('keyup', function(e) {
4560                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561                         this.toggleHeaderInput(false)
4562                     }
4563                 }, this);
4564             this.headerEditEl.on('blur', function(e) {
4565                 this.toggleHeaderInput(false)
4566             },this);
4567         }
4568
4569     },
4570   
4571
4572     resize : function()
4573     {
4574         this.maskEl.setSize(
4575             Roo.lib.Dom.getViewWidth(true),
4576             Roo.lib.Dom.getViewHeight(true)
4577         );
4578         
4579         if (this.fitwindow) {
4580             
4581            this.dialogEl.setStyle( { 'max-width' : '100%' });
4582             this.setSize(
4583                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585             );
4586             return;
4587         }
4588         
4589         if(this.max_width !== 0) {
4590             
4591             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592             
4593             if(this.height) {
4594                 this.setSize(w, this.height);
4595                 return;
4596             }
4597             
4598             if(this.max_height) {
4599                 this.setSize(w,Math.min(
4600                     this.max_height,
4601                     Roo.lib.Dom.getViewportHeight(true) - 60
4602                 ));
4603                 
4604                 return;
4605             }
4606             
4607             if(!this.fit_content) {
4608                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609                 return;
4610             }
4611             
4612             this.setSize(w, Math.min(
4613                 60 +
4614                 this.headerEl.getHeight() + 
4615                 this.footerEl.getHeight() + 
4616                 this.getChildHeight(this.bodyEl.dom.childNodes),
4617                 Roo.lib.Dom.getViewportHeight(true) - 60)
4618             );
4619         }
4620         
4621     },
4622
4623     setSize : function(w,h)
4624     {
4625         if (!w && !h) {
4626             return;
4627         }
4628         
4629         this.resizeTo(w,h);
4630     },
4631
4632     show : function() {
4633
4634         if (!this.rendered) {
4635             this.render();
4636         }
4637         this.toggleHeaderInput(false);
4638         //this.el.setStyle('display', 'block');
4639         this.el.removeClass('hideing');
4640         this.el.dom.style.display='block';
4641         
4642         Roo.get(document.body).addClass('modal-open');
4643  
4644         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4645             
4646             (function(){
4647                 this.el.addClass('show');
4648                 this.el.addClass('in');
4649             }).defer(50, this);
4650         }else{
4651             this.el.addClass('show');
4652             this.el.addClass('in');
4653         }
4654
4655         // not sure how we can show data in here..
4656         //if (this.tmpl) {
4657         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658         //}
4659
4660         Roo.get(document.body).addClass("x-body-masked");
4661         
4662         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4663         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664         this.maskEl.dom.style.display = 'block';
4665         this.maskEl.addClass('show');
4666         
4667         
4668         this.resize();
4669         
4670         this.fireEvent('show', this);
4671
4672         // set zindex here - otherwise it appears to be ignored...
4673         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674
4675         (function () {
4676             this.items.forEach( function(e) {
4677                 e.layout ? e.layout() : false;
4678
4679             });
4680         }).defer(100,this);
4681
4682     },
4683     hide : function()
4684     {
4685         if(this.fireEvent("beforehide", this) !== false){
4686             
4687             this.maskEl.removeClass('show');
4688             
4689             this.maskEl.dom.style.display = '';
4690             Roo.get(document.body).removeClass("x-body-masked");
4691             this.el.removeClass('in');
4692             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4693
4694             if(this.animate){ // why
4695                 this.el.addClass('hideing');
4696                 this.el.removeClass('show');
4697                 (function(){
4698                     if (!this.el.hasClass('hideing')) {
4699                         return; // it's been shown again...
4700                     }
4701                     
4702                     this.el.dom.style.display='';
4703
4704                     Roo.get(document.body).removeClass('modal-open');
4705                     this.el.removeClass('hideing');
4706                 }).defer(150,this);
4707                 
4708             }else{
4709                 this.el.removeClass('show');
4710                 this.el.dom.style.display='';
4711                 Roo.get(document.body).removeClass('modal-open');
4712
4713             }
4714             this.fireEvent('hide', this);
4715         }
4716     },
4717     isVisible : function()
4718     {
4719         
4720         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4721         
4722     },
4723
4724     addButton : function(str, cb)
4725     {
4726
4727
4728         var b = Roo.apply({}, { html : str } );
4729         b.xns = b.xns || Roo.bootstrap;
4730         b.xtype = b.xtype || 'Button';
4731         if (typeof(b.listeners) == 'undefined') {
4732             b.listeners = { click : cb.createDelegate(this)  };
4733         }
4734
4735         var btn = Roo.factory(b);
4736
4737         btn.render(this.getButtonContainer());
4738
4739         return btn;
4740
4741     },
4742
4743     setDefaultButton : function(btn)
4744     {
4745         //this.el.select('.modal-footer').()
4746     },
4747
4748     resizeTo: function(w,h)
4749     {
4750         this.dialogEl.setWidth(w);
4751         
4752         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4753
4754         this.bodyEl.setHeight(h - diff);
4755         
4756         this.fireEvent('resize', this);
4757     },
4758     
4759     setContentSize  : function(w, h)
4760     {
4761
4762     },
4763     onButtonClick: function(btn,e)
4764     {
4765         //Roo.log([a,b,c]);
4766         this.fireEvent('btnclick', btn.name, e);
4767     },
4768      /**
4769      * Set the title of the Dialog
4770      * @param {String} str new Title
4771      */
4772     setTitle: function(str) {
4773         this.titleEl.dom.innerHTML = str;
4774         this.title = str;
4775     },
4776     /**
4777      * Set the body of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setBody: function(str) {
4781         this.bodyEl.dom.innerHTML = str;
4782     },
4783     /**
4784      * Set the body of the Dialog using the template
4785      * @param {Obj} data - apply this data to the template and replace the body contents.
4786      */
4787     applyBody: function(obj)
4788     {
4789         if (!this.tmpl) {
4790             Roo.log("Error - using apply Body without a template");
4791             //code
4792         }
4793         this.tmpl.overwrite(this.bodyEl, obj);
4794     },
4795     
4796     getChildHeight : function(child_nodes)
4797     {
4798         if(
4799             !child_nodes ||
4800             child_nodes.length == 0
4801         ) {
4802             return 0;
4803         }
4804         
4805         var child_height = 0;
4806         
4807         for(var i = 0; i < child_nodes.length; i++) {
4808             
4809             /*
4810             * for modal with tabs...
4811             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4812                 
4813                 var layout_childs = child_nodes[i].childNodes;
4814                 
4815                 for(var j = 0; j < layout_childs.length; j++) {
4816                     
4817                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4818                         
4819                         var layout_body_childs = layout_childs[j].childNodes;
4820                         
4821                         for(var k = 0; k < layout_body_childs.length; k++) {
4822                             
4823                             if(layout_body_childs[k].classList.contains('navbar')) {
4824                                 child_height += layout_body_childs[k].offsetHeight;
4825                                 continue;
4826                             }
4827                             
4828                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4829                                 
4830                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4831                                 
4832                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4833                                     
4834                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4835                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4836                                         continue;
4837                                     }
4838                                     
4839                                 }
4840                                 
4841                             }
4842                             
4843                         }
4844                     }
4845                 }
4846                 continue;
4847             }
4848             */
4849             
4850             child_height += child_nodes[i].offsetHeight;
4851             // Roo.log(child_nodes[i].offsetHeight);
4852         }
4853         
4854         return child_height;
4855     },
4856     toggleHeaderInput : function(is_edit)
4857     {
4858         if (!this.editableTitle) {
4859             return; // not editable.
4860         }
4861         if (is_edit && this.is_header_editing) {
4862             return; // already editing..
4863         }
4864         if (is_edit) {
4865     
4866             this.headerEditEl.dom.value = this.title;
4867             this.headerEditEl.removeClass('d-none');
4868             this.headerEditEl.dom.focus();
4869             this.titleEl.addClass('d-none');
4870             
4871             this.is_header_editing = true;
4872             return
4873         }
4874         // flip back to not editing.
4875         this.title = this.headerEditEl.dom.value;
4876         this.headerEditEl.addClass('d-none');
4877         this.titleEl.removeClass('d-none');
4878         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4879         this.is_header_editing = false;
4880         this.fireEvent('titlechanged', this, this.title);
4881     
4882             
4883         
4884     }
4885
4886 });
4887
4888
4889 Roo.apply(Roo.bootstrap.Modal,  {
4890     /**
4891          * Button config that displays a single OK button
4892          * @type Object
4893          */
4894         OK :  [{
4895             name : 'ok',
4896             weight : 'primary',
4897             html : 'OK'
4898         }],
4899         /**
4900          * Button config that displays Yes and No buttons
4901          * @type Object
4902          */
4903         YESNO : [
4904             {
4905                 name  : 'no',
4906                 html : 'No'
4907             },
4908             {
4909                 name  :'yes',
4910                 weight : 'primary',
4911                 html : 'Yes'
4912             }
4913         ],
4914
4915         /**
4916          * Button config that displays OK and Cancel buttons
4917          * @type Object
4918          */
4919         OKCANCEL : [
4920             {
4921                name : 'cancel',
4922                 html : 'Cancel'
4923             },
4924             {
4925                 name : 'ok',
4926                 weight : 'primary',
4927                 html : 'OK'
4928             }
4929         ],
4930         /**
4931          * Button config that displays Yes, No and Cancel buttons
4932          * @type Object
4933          */
4934         YESNOCANCEL : [
4935             {
4936                 name : 'yes',
4937                 weight : 'primary',
4938                 html : 'Yes'
4939             },
4940             {
4941                 name : 'no',
4942                 html : 'No'
4943             },
4944             {
4945                 name : 'cancel',
4946                 html : 'Cancel'
4947             }
4948         ],
4949         
4950         zIndex : 10001
4951 });
4952
4953 /*
4954  * - LGPL
4955  *
4956  * messagebox - can be used as a replace
4957  * 
4958  */
4959 /**
4960  * @class Roo.MessageBox
4961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4962  * Example usage:
4963  *<pre><code>
4964 // Basic alert:
4965 Roo.Msg.alert('Status', 'Changes saved successfully.');
4966
4967 // Prompt for user data:
4968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4969     if (btn == 'ok'){
4970         // process text value...
4971     }
4972 });
4973
4974 // Show a dialog using config options:
4975 Roo.Msg.show({
4976    title:'Save Changes?',
4977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4978    buttons: Roo.Msg.YESNOCANCEL,
4979    fn: processResult,
4980    animEl: 'elId'
4981 });
4982 </code></pre>
4983  * @singleton
4984  */
4985 Roo.bootstrap.MessageBox = function(){
4986     var dlg, opt, mask, waitTimer;
4987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4988     var buttons, activeTextEl, bwidth;
4989
4990     
4991     // private
4992     var handleButton = function(button){
4993         dlg.hide();
4994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4995     };
4996
4997     // private
4998     var handleHide = function(){
4999         if(opt && opt.cls){
5000             dlg.el.removeClass(opt.cls);
5001         }
5002         //if(waitTimer){
5003         //    Roo.TaskMgr.stop(waitTimer);
5004         //    waitTimer = null;
5005         //}
5006     };
5007
5008     // private
5009     var updateButtons = function(b){
5010         var width = 0;
5011         if(!b){
5012             buttons["ok"].hide();
5013             buttons["cancel"].hide();
5014             buttons["yes"].hide();
5015             buttons["no"].hide();
5016             dlg.footerEl.hide();
5017             
5018             return width;
5019         }
5020         dlg.footerEl.show();
5021         for(var k in buttons){
5022             if(typeof buttons[k] != "function"){
5023                 if(b[k]){
5024                     buttons[k].show();
5025                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5026                     width += buttons[k].el.getWidth()+15;
5027                 }else{
5028                     buttons[k].hide();
5029                 }
5030             }
5031         }
5032         return width;
5033     };
5034
5035     // private
5036     var handleEsc = function(d, k, e){
5037         if(opt && opt.closable !== false){
5038             dlg.hide();
5039         }
5040         if(e){
5041             e.stopEvent();
5042         }
5043     };
5044
5045     return {
5046         /**
5047          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5048          * @return {Roo.BasicDialog} The BasicDialog element
5049          */
5050         getDialog : function(){
5051            if(!dlg){
5052                 dlg = new Roo.bootstrap.Modal( {
5053                     //draggable: true,
5054                     //resizable:false,
5055                     //constraintoviewport:false,
5056                     //fixedcenter:true,
5057                     //collapsible : false,
5058                     //shim:true,
5059                     //modal: true,
5060                 //    width: 'auto',
5061                   //  height:100,
5062                     //buttonAlign:"center",
5063                     closeClick : function(){
5064                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065                             handleButton("no");
5066                         }else{
5067                             handleButton("cancel");
5068                         }
5069                     }
5070                 });
5071                 dlg.render();
5072                 dlg.on("hide", handleHide);
5073                 mask = dlg.mask;
5074                 //dlg.addKeyListener(27, handleEsc);
5075                 buttons = {};
5076                 this.buttons = buttons;
5077                 var bt = this.buttonText;
5078                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5079                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5080                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5081                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5082                 //Roo.log(buttons);
5083                 bodyEl = dlg.bodyEl.createChild({
5084
5085                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5086                         '<textarea class="roo-mb-textarea"></textarea>' +
5087                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5088                 });
5089                 msgEl = bodyEl.dom.firstChild;
5090                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5091                 textboxEl.enableDisplayMode();
5092                 textboxEl.addKeyListener([10,13], function(){
5093                     if(dlg.isVisible() && opt && opt.buttons){
5094                         if(opt.buttons.ok){
5095                             handleButton("ok");
5096                         }else if(opt.buttons.yes){
5097                             handleButton("yes");
5098                         }
5099                     }
5100                 });
5101                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5102                 textareaEl.enableDisplayMode();
5103                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5104                 progressEl.enableDisplayMode();
5105                 
5106                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5107                 var pf = progressEl.dom.firstChild;
5108                 if (pf) {
5109                     pp = Roo.get(pf.firstChild);
5110                     pp.setHeight(pf.offsetHeight);
5111                 }
5112                 
5113             }
5114             return dlg;
5115         },
5116
5117         /**
5118          * Updates the message box body text
5119          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5120          * the XHTML-compliant non-breaking space character '&amp;#160;')
5121          * @return {Roo.MessageBox} This message box
5122          */
5123         updateText : function(text)
5124         {
5125             if(!dlg.isVisible() && !opt.width){
5126                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5127                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5128             }
5129             msgEl.innerHTML = text || '&#160;';
5130       
5131             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5132             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5133             var w = Math.max(
5134                     Math.min(opt.width || cw , this.maxWidth), 
5135                     Math.max(opt.minWidth || this.minWidth, bwidth)
5136             );
5137             if(opt.prompt){
5138                 activeTextEl.setWidth(w);
5139             }
5140             if(dlg.isVisible()){
5141                 dlg.fixedcenter = false;
5142             }
5143             // to big, make it scroll. = But as usual stupid IE does not support
5144             // !important..
5145             
5146             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5147                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5148                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5149             } else {
5150                 bodyEl.dom.style.height = '';
5151                 bodyEl.dom.style.overflowY = '';
5152             }
5153             if (cw > w) {
5154                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5155             } else {
5156                 bodyEl.dom.style.overflowX = '';
5157             }
5158             
5159             dlg.setContentSize(w, bodyEl.getHeight());
5160             if(dlg.isVisible()){
5161                 dlg.fixedcenter = true;
5162             }
5163             return this;
5164         },
5165
5166         /**
5167          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5168          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5169          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5170          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5171          * @return {Roo.MessageBox} This message box
5172          */
5173         updateProgress : function(value, text){
5174             if(text){
5175                 this.updateText(text);
5176             }
5177             
5178             if (pp) { // weird bug on my firefox - for some reason this is not defined
5179                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5180                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5181             }
5182             return this;
5183         },        
5184
5185         /**
5186          * Returns true if the message box is currently displayed
5187          * @return {Boolean} True if the message box is visible, else false
5188          */
5189         isVisible : function(){
5190             return dlg && dlg.isVisible();  
5191         },
5192
5193         /**
5194          * Hides the message box if it is displayed
5195          */
5196         hide : function(){
5197             if(this.isVisible()){
5198                 dlg.hide();
5199             }  
5200         },
5201
5202         /**
5203          * Displays a new message box, or reinitializes an existing message box, based on the config options
5204          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5205          * The following config object properties are supported:
5206          * <pre>
5207 Property    Type             Description
5208 ----------  ---------------  ------------------------------------------------------------------------------------
5209 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5210                                    closes (defaults to undefined)
5211 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5212                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5213 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5214                                    progress and wait dialogs will ignore this property and always hide the
5215                                    close button as they can only be closed programmatically.
5216 cls               String           A custom CSS class to apply to the message box element
5217 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5218                                    displayed (defaults to 75)
5219 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5220                                    function will be btn (the name of the button that was clicked, if applicable,
5221                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5222                                    Progress and wait dialogs will ignore this option since they do not respond to
5223                                    user actions and can only be closed programmatically, so any required function
5224                                    should be called by the same code after it closes the dialog.
5225 icon              String           A CSS class that provides a background image to be used as an icon for
5226                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5227 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5228 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5229 modal             Boolean          False to allow user interaction with the page while the message box is
5230                                    displayed (defaults to true)
5231 msg               String           A string that will replace the existing message box body text (defaults
5232                                    to the XHTML-compliant non-breaking space character '&#160;')
5233 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5234 progress          Boolean          True to display a progress bar (defaults to false)
5235 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5236 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5237 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5238 title             String           The title text
5239 value             String           The string value to set into the active textbox element if displayed
5240 wait              Boolean          True to display a progress bar (defaults to false)
5241 width             Number           The width of the dialog in pixels
5242 </pre>
5243          *
5244          * Example usage:
5245          * <pre><code>
5246 Roo.Msg.show({
5247    title: 'Address',
5248    msg: 'Please enter your address:',
5249    width: 300,
5250    buttons: Roo.MessageBox.OKCANCEL,
5251    multiline: true,
5252    fn: saveAddress,
5253    animEl: 'addAddressBtn'
5254 });
5255 </code></pre>
5256          * @param {Object} config Configuration options
5257          * @return {Roo.MessageBox} This message box
5258          */
5259         show : function(options)
5260         {
5261             
5262             // this causes nightmares if you show one dialog after another
5263             // especially on callbacks..
5264              
5265             if(this.isVisible()){
5266                 
5267                 this.hide();
5268                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5269                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5270                 Roo.log("New Dialog Message:" +  options.msg )
5271                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5272                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273                 
5274             }
5275             var d = this.getDialog();
5276             opt = options;
5277             d.setTitle(opt.title || "&#160;");
5278             d.closeEl.setDisplayed(opt.closable !== false);
5279             activeTextEl = textboxEl;
5280             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5281             if(opt.prompt){
5282                 if(opt.multiline){
5283                     textboxEl.hide();
5284                     textareaEl.show();
5285                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5286                         opt.multiline : this.defaultTextHeight);
5287                     activeTextEl = textareaEl;
5288                 }else{
5289                     textboxEl.show();
5290                     textareaEl.hide();
5291                 }
5292             }else{
5293                 textboxEl.hide();
5294                 textareaEl.hide();
5295             }
5296             progressEl.setDisplayed(opt.progress === true);
5297             if (opt.progress) {
5298                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5299             }
5300             this.updateProgress(0);
5301             activeTextEl.dom.value = opt.value || "";
5302             if(opt.prompt){
5303                 dlg.setDefaultButton(activeTextEl);
5304             }else{
5305                 var bs = opt.buttons;
5306                 var db = null;
5307                 if(bs && bs.ok){
5308                     db = buttons["ok"];
5309                 }else if(bs && bs.yes){
5310                     db = buttons["yes"];
5311                 }
5312                 dlg.setDefaultButton(db);
5313             }
5314             bwidth = updateButtons(opt.buttons);
5315             this.updateText(opt.msg);
5316             if(opt.cls){
5317                 d.el.addClass(opt.cls);
5318             }
5319             d.proxyDrag = opt.proxyDrag === true;
5320             d.modal = opt.modal !== false;
5321             d.mask = opt.modal !== false ? mask : false;
5322             if(!d.isVisible()){
5323                 // force it to the end of the z-index stack so it gets a cursor in FF
5324                 document.body.appendChild(dlg.el.dom);
5325                 d.animateTarget = null;
5326                 d.show(options.animEl);
5327             }
5328             return this;
5329         },
5330
5331         /**
5332          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5333          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5334          * and closing the message box when the process is complete.
5335          * @param {String} title The title bar text
5336          * @param {String} msg The message box body text
5337          * @return {Roo.MessageBox} This message box
5338          */
5339         progress : function(title, msg){
5340             this.show({
5341                 title : title,
5342                 msg : msg,
5343                 buttons: false,
5344                 progress:true,
5345                 closable:false,
5346                 minWidth: this.minProgressWidth,
5347                 modal : true
5348             });
5349             return this;
5350         },
5351
5352         /**
5353          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5354          * If a callback function is passed it will be called after the user clicks the button, and the
5355          * id of the button that was clicked will be passed as the only parameter to the callback
5356          * (could also be the top-right close button).
5357          * @param {String} title The title bar text
5358          * @param {String} msg The message box body text
5359          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5360          * @param {Object} scope (optional) The scope of the callback function
5361          * @return {Roo.MessageBox} This message box
5362          */
5363         alert : function(title, msg, fn, scope)
5364         {
5365             this.show({
5366                 title : title,
5367                 msg : msg,
5368                 buttons: this.OK,
5369                 fn: fn,
5370                 closable : false,
5371                 scope : scope,
5372                 modal : true
5373             });
5374             return this;
5375         },
5376
5377         /**
5378          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5379          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5380          * You are responsible for closing the message box when the process is complete.
5381          * @param {String} msg The message box body text
5382          * @param {String} title (optional) The title bar text
5383          * @return {Roo.MessageBox} This message box
5384          */
5385         wait : function(msg, title){
5386             this.show({
5387                 title : title,
5388                 msg : msg,
5389                 buttons: false,
5390                 closable:false,
5391                 progress:true,
5392                 modal:true,
5393                 width:300,
5394                 wait:true
5395             });
5396             waitTimer = Roo.TaskMgr.start({
5397                 run: function(i){
5398                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5399                 },
5400                 interval: 1000
5401             });
5402             return this;
5403         },
5404
5405         /**
5406          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5407          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5408          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5409          * @param {String} title The title bar text
5410          * @param {String} msg The message box body text
5411          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5412          * @param {Object} scope (optional) The scope of the callback function
5413          * @return {Roo.MessageBox} This message box
5414          */
5415         confirm : function(title, msg, fn, scope){
5416             this.show({
5417                 title : title,
5418                 msg : msg,
5419                 buttons: this.YESNO,
5420                 fn: fn,
5421                 scope : scope,
5422                 modal : true
5423             });
5424             return this;
5425         },
5426
5427         /**
5428          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5429          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5430          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5431          * (could also be the top-right close button) and the text that was entered will be passed as the two
5432          * parameters to the callback.
5433          * @param {String} title The title bar text
5434          * @param {String} msg The message box body text
5435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5436          * @param {Object} scope (optional) The scope of the callback function
5437          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5438          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5439          * @return {Roo.MessageBox} This message box
5440          */
5441         prompt : function(title, msg, fn, scope, multiline){
5442             this.show({
5443                 title : title,
5444                 msg : msg,
5445                 buttons: this.OKCANCEL,
5446                 fn: fn,
5447                 minWidth:250,
5448                 scope : scope,
5449                 prompt:true,
5450                 multiline: multiline,
5451                 modal : true
5452             });
5453             return this;
5454         },
5455
5456         /**
5457          * Button config that displays a single OK button
5458          * @type Object
5459          */
5460         OK : {ok:true},
5461         /**
5462          * Button config that displays Yes and No buttons
5463          * @type Object
5464          */
5465         YESNO : {yes:true, no:true},
5466         /**
5467          * Button config that displays OK and Cancel buttons
5468          * @type Object
5469          */
5470         OKCANCEL : {ok:true, cancel:true},
5471         /**
5472          * Button config that displays Yes, No and Cancel buttons
5473          * @type Object
5474          */
5475         YESNOCANCEL : {yes:true, no:true, cancel:true},
5476
5477         /**
5478          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479          * @type Number
5480          */
5481         defaultTextHeight : 75,
5482         /**
5483          * The maximum width in pixels of the message box (defaults to 600)
5484          * @type Number
5485          */
5486         maxWidth : 600,
5487         /**
5488          * The minimum width in pixels of the message box (defaults to 100)
5489          * @type Number
5490          */
5491         minWidth : 100,
5492         /**
5493          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5494          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495          * @type Number
5496          */
5497         minProgressWidth : 250,
5498         /**
5499          * An object containing the default button text strings that can be overriden for localized language support.
5500          * Supported properties are: ok, cancel, yes and no.
5501          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5502          * @type Object
5503          */
5504         buttonText : {
5505             ok : "OK",
5506             cancel : "Cancel",
5507             yes : "Yes",
5508             no : "No"
5509         }
5510     };
5511 }();
5512
5513 /**
5514  * Shorthand for {@link Roo.MessageBox}
5515  */
5516 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5517 Roo.Msg = Roo.Msg || Roo.MessageBox;
5518 Roo.bootstrap.nav = {};
5519
5520 /*
5521  * - LGPL
5522  *
5523  * navbar
5524  * 
5525  */
5526
5527 /**
5528  * @class Roo.bootstrap.nav.Bar
5529  * @extends Roo.bootstrap.Component
5530  * @abstract
5531  * Bootstrap Navbar class
5532
5533  * @constructor
5534  * Create a new Navbar
5535  * @param {Object} config The config object
5536  */
5537
5538
5539 Roo.bootstrap.nav.Bar = function(config){
5540     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5541     this.addEvents({
5542         // raw events
5543         /**
5544          * @event beforetoggle
5545          * Fire before toggle the menu
5546          * @param {Roo.EventObject} e
5547          */
5548         "beforetoggle" : true
5549     });
5550 };
5551
5552 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5553     
5554     
5555    
5556     // private
5557     navItems : false,
5558     loadMask : false,
5559     
5560     
5561     getAutoCreate : function(){
5562         
5563         
5564         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5565         
5566     },
5567     
5568     initEvents :function ()
5569     {
5570         //Roo.log(this.el.select('.navbar-toggle',true));
5571         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5572         
5573         var mark = {
5574             tag: "div",
5575             cls:"x-dlg-mask"
5576         };
5577         
5578         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5579         
5580         var size = this.el.getSize();
5581         this.maskEl.setSize(size.width, size.height);
5582         this.maskEl.enableDisplayMode("block");
5583         this.maskEl.hide();
5584         
5585         if(this.loadMask){
5586             this.maskEl.show();
5587         }
5588     },
5589     
5590     
5591     getChildContainer : function()
5592     {
5593         if (this.el && this.el.select('.collapse').getCount()) {
5594             return this.el.select('.collapse',true).first();
5595         }
5596         
5597         return this.el;
5598     },
5599     
5600     mask : function()
5601     {
5602         this.maskEl.show();
5603     },
5604     
5605     unmask : function()
5606     {
5607         this.maskEl.hide();
5608     },
5609     onToggle : function()
5610     {
5611         
5612         if(this.fireEvent('beforetoggle', this) === false){
5613             return;
5614         }
5615         var ce = this.el.select('.navbar-collapse',true).first();
5616       
5617         if (!ce.hasClass('show')) {
5618            this.expand();
5619         } else {
5620             this.collapse();
5621         }
5622         
5623         
5624     
5625     },
5626     /**
5627      * Expand the navbar pulldown 
5628      */
5629     expand : function ()
5630     {
5631        
5632         var ce = this.el.select('.navbar-collapse',true).first();
5633         if (ce.hasClass('collapsing')) {
5634             return;
5635         }
5636         ce.dom.style.height = '';
5637                // show it...
5638         ce.addClass('in'); // old...
5639         ce.removeClass('collapse');
5640         ce.addClass('show');
5641         var h = ce.getHeight();
5642         Roo.log(h);
5643         ce.removeClass('show');
5644         // at this point we should be able to see it..
5645         ce.addClass('collapsing');
5646         
5647         ce.setHeight(0); // resize it ...
5648         ce.on('transitionend', function() {
5649             //Roo.log('done transition');
5650             ce.removeClass('collapsing');
5651             ce.addClass('show');
5652             ce.removeClass('collapse');
5653
5654             ce.dom.style.height = '';
5655         }, this, { single: true} );
5656         ce.setHeight(h);
5657         ce.dom.scrollTop = 0;
5658     },
5659     /**
5660      * Collapse the navbar pulldown 
5661      */
5662     collapse : function()
5663     {
5664          var ce = this.el.select('.navbar-collapse',true).first();
5665        
5666         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5667             // it's collapsed or collapsing..
5668             return;
5669         }
5670         ce.removeClass('in'); // old...
5671         ce.setHeight(ce.getHeight());
5672         ce.removeClass('show');
5673         ce.addClass('collapsing');
5674         
5675         ce.on('transitionend', function() {
5676             ce.dom.style.height = '';
5677             ce.removeClass('collapsing');
5678             ce.addClass('collapse');
5679         }, this, { single: true} );
5680         ce.setHeight(0);
5681     }
5682     
5683     
5684     
5685 });
5686
5687
5688
5689  
5690
5691  /*
5692  * - LGPL
5693  *
5694  * navbar
5695  * 
5696  */
5697
5698 /**
5699  * @class Roo.bootstrap.nav.Simplebar
5700  * @extends Roo.bootstrap.nav.Bar
5701  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5702  * Bootstrap Sidebar class
5703  *
5704  * @cfg {Boolean} inverse is inverted color
5705  * 
5706  * @cfg {String} type (nav | pills | tabs)
5707  * @cfg {Boolean} arrangement stacked | justified
5708  * @cfg {String} align (left | right) alignment
5709  * 
5710  * @cfg {Boolean} main (true|false) main nav bar? default false
5711  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5712  * 
5713  * @cfg {String} tag (header|footer|nav|div) default is nav 
5714
5715  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5716  * 
5717  * 
5718  * @constructor
5719  * Create a new Sidebar
5720  * @param {Object} config The config object
5721  */
5722
5723
5724 Roo.bootstrap.nav.Simplebar = function(config){
5725     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5726 };
5727
5728 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5729     
5730     inverse: false,
5731     
5732     type: false,
5733     arrangement: '',
5734     align : false,
5735     
5736     weight : 'light',
5737     
5738     main : false,
5739     
5740     
5741     tag : false,
5742     
5743     
5744     getAutoCreate : function(){
5745         
5746         
5747         var cfg = {
5748             tag : this.tag || 'div',
5749             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5750         };
5751         if (['light','white'].indexOf(this.weight) > -1) {
5752             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5753         }
5754         cfg.cls += ' bg-' + this.weight;
5755         
5756         if (this.inverse) {
5757             cfg.cls += ' navbar-inverse';
5758             
5759         }
5760         
5761         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5762         
5763         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5764             return cfg;
5765         }
5766         
5767         
5768     
5769         
5770         cfg.cn = [
5771             {
5772                 cls: 'nav nav-' + this.xtype,
5773                 tag : 'ul'
5774             }
5775         ];
5776         
5777          
5778         this.type = this.type || 'nav';
5779         if (['tabs','pills'].indexOf(this.type) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.type
5781         
5782         
5783         } else {
5784             if (this.type!=='nav') {
5785                 Roo.log('nav type must be nav/tabs/pills')
5786             }
5787             cfg.cn[0].cls += ' navbar-nav'
5788         }
5789         
5790         
5791         
5792         
5793         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5794             cfg.cn[0].cls += ' nav-' + this.arrangement;
5795         }
5796         
5797         
5798         if (this.align === 'right') {
5799             cfg.cn[0].cls += ' navbar-right';
5800         }
5801         
5802         
5803         
5804         
5805         return cfg;
5806     
5807         
5808     }
5809     
5810     
5811     
5812 });
5813
5814
5815
5816  
5817
5818  
5819        /*
5820  * - LGPL
5821  *
5822  * navbar
5823  * navbar-fixed-top
5824  * navbar-expand-md  fixed-top 
5825  */
5826
5827 /**
5828  * @class Roo.bootstrap.nav.Headerbar
5829  * @extends Roo.bootstrap.nav.Simplebar
5830  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5831  * Bootstrap Sidebar class
5832  *
5833  * @cfg {String} brand what is brand
5834  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5835  * @cfg {String} brand_href href of the brand
5836  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5837  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5838  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5839  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5840  * 
5841  * @constructor
5842  * Create a new Sidebar
5843  * @param {Object} config The config object
5844  */
5845
5846
5847 Roo.bootstrap.nav.Headerbar = function(config){
5848     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5849       
5850 };
5851
5852 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5853     
5854     position: '',
5855     brand: '',
5856     brand_href: false,
5857     srButton : true,
5858     autohide : false,
5859     desktopCenter : false,
5860    
5861     
5862     getAutoCreate : function(){
5863         
5864         var   cfg = {
5865             tag: this.nav || 'nav',
5866             cls: 'navbar navbar-expand-md',
5867             role: 'navigation',
5868             cn: []
5869         };
5870         
5871         var cn = cfg.cn;
5872         if (this.desktopCenter) {
5873             cn.push({cls : 'container', cn : []});
5874             cn = cn[0].cn;
5875         }
5876         
5877         if(this.srButton){
5878             var btn = {
5879                 tag: 'button',
5880                 type: 'button',
5881                 cls: 'navbar-toggle navbar-toggler',
5882                 'data-toggle': 'collapse',
5883                 cn: [
5884                     {
5885                         tag: 'span',
5886                         cls: 'sr-only',
5887                         html: 'Toggle navigation'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar navbar-toggler-icon'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     },
5897                     {
5898                         tag: 'span',
5899                         cls: 'icon-bar'
5900                     }
5901                 ]
5902             };
5903             
5904             cn.push( Roo.bootstrap.version == 4 ? btn : {
5905                 tag: 'div',
5906                 cls: 'navbar-header',
5907                 cn: [
5908                     btn
5909                 ]
5910             });
5911         }
5912         
5913         cn.push({
5914             tag: 'div',
5915             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5916             cn : []
5917         });
5918         
5919         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5920         
5921         if (['light','white'].indexOf(this.weight) > -1) {
5922             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5923         }
5924         cfg.cls += ' bg-' + this.weight;
5925         
5926         
5927         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5928             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5929             
5930             // tag can override this..
5931             
5932             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5933         }
5934         
5935         if (this.brand !== '') {
5936             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5937             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5938                 tag: 'a',
5939                 href: this.brand_href ? this.brand_href : '#',
5940                 cls: 'navbar-brand',
5941                 cn: [
5942                 this.brand
5943                 ]
5944             });
5945         }
5946         
5947         if(this.main){
5948             cfg.cls += ' main-nav';
5949         }
5950         
5951         
5952         return cfg;
5953
5954         
5955     },
5956     getHeaderChildContainer : function()
5957     {
5958         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5959             return this.el.select('.navbar-header',true).first();
5960         }
5961         
5962         return this.getChildContainer();
5963     },
5964     
5965     getChildContainer : function()
5966     {
5967          
5968         return this.el.select('.roo-navbar-collapse',true).first();
5969          
5970         
5971     },
5972     
5973     initEvents : function()
5974     {
5975         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5976         
5977         if (this.autohide) {
5978             
5979             var prevScroll = 0;
5980             var ft = this.el;
5981             
5982             Roo.get(document).on('scroll',function(e) {
5983                 var ns = Roo.get(document).getScroll().top;
5984                 var os = prevScroll;
5985                 prevScroll = ns;
5986                 
5987                 if(ns > os){
5988                     ft.removeClass('slideDown');
5989                     ft.addClass('slideUp');
5990                     return;
5991                 }
5992                 ft.removeClass('slideUp');
5993                 ft.addClass('slideDown');
5994                  
5995               
5996           },this);
5997         }
5998     }    
5999     
6000 });
6001
6002
6003
6004  
6005
6006  /*
6007  * - LGPL
6008  *
6009  * navbar
6010  * 
6011  */
6012
6013 /**
6014  * @class Roo.bootstrap.nav.Sidebar
6015  * @extends Roo.bootstrap.nav.Bar
6016  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6017  * Bootstrap Sidebar class
6018  * 
6019  * @constructor
6020  * Create a new Sidebar
6021  * @param {Object} config The config object
6022  */
6023
6024
6025 Roo.bootstrap.nav.Sidebar = function(config){
6026     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6027 };
6028
6029 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6030     
6031     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6032     
6033     getAutoCreate : function(){
6034         
6035         
6036         return  {
6037             tag: 'div',
6038             cls: 'sidebar sidebar-nav'
6039         };
6040     
6041         
6042     }
6043     
6044     
6045     
6046 });
6047
6048
6049
6050  
6051
6052  /*
6053  * - LGPL
6054  *
6055  * nav group
6056  * 
6057  */
6058
6059 /**
6060  * @class Roo.bootstrap.nav.Group
6061  * @extends Roo.bootstrap.Component
6062  * @children Roo.bootstrap.nav.Item
6063  * Bootstrap NavGroup class
6064  * @cfg {String} align (left|right)
6065  * @cfg {Boolean} inverse
6066  * @cfg {String} type (nav|pills|tab) default nav
6067  * @cfg {String} navId - reference Id for navbar.
6068  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6069  * 
6070  * @constructor
6071  * Create a new nav group
6072  * @param {Object} config The config object
6073  */
6074
6075 Roo.bootstrap.nav.Group = function(config){
6076     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6077     this.navItems = [];
6078    
6079     Roo.bootstrap.nav.Group.register(this);
6080      this.addEvents({
6081         /**
6082              * @event changed
6083              * Fires when the active item changes
6084              * @param {Roo.bootstrap.nav.Group} this
6085              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6086              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6087          */
6088         'changed': true
6089      });
6090     
6091 };
6092
6093 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6094     
6095     align: '',
6096     inverse: false,
6097     form: false,
6098     type: 'nav',
6099     navId : '',
6100     // private
6101     pilltype : true,
6102     
6103     navItems : false, 
6104     
6105     getAutoCreate : function()
6106     {
6107         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6108         
6109         cfg = {
6110             tag : 'ul',
6111             cls: 'nav' 
6112         };
6113         if (Roo.bootstrap.version == 4) {
6114             if (['tabs','pills'].indexOf(this.type) != -1) {
6115                 cfg.cls += ' nav-' + this.type; 
6116             } else {
6117                 // trying to remove so header bar can right align top?
6118                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6119                     // do not use on header bar... 
6120                     cfg.cls += ' navbar-nav';
6121                 }
6122             }
6123             
6124         } else {
6125             if (['tabs','pills'].indexOf(this.type) != -1) {
6126                 cfg.cls += ' nav-' + this.type
6127             } else {
6128                 if (this.type !== 'nav') {
6129                     Roo.log('nav type must be nav/tabs/pills')
6130                 }
6131                 cfg.cls += ' navbar-nav'
6132             }
6133         }
6134         
6135         if (this.parent() && this.parent().sidebar) {
6136             cfg = {
6137                 tag: 'ul',
6138                 cls: 'dashboard-menu sidebar-menu'
6139             };
6140             
6141             return cfg;
6142         }
6143         
6144         if (this.form === true) {
6145             cfg = {
6146                 tag: 'form',
6147                 cls: 'navbar-form form-inline'
6148             };
6149             //nav navbar-right ml-md-auto
6150             if (this.align === 'right') {
6151                 cfg.cls += ' navbar-right ml-md-auto';
6152             } else {
6153                 cfg.cls += ' navbar-left';
6154             }
6155         }
6156         
6157         if (this.align === 'right') {
6158             cfg.cls += ' navbar-right ml-md-auto';
6159         } else {
6160             cfg.cls += ' mr-auto';
6161         }
6162         
6163         if (this.inverse) {
6164             cfg.cls += ' navbar-inverse';
6165             
6166         }
6167         
6168         
6169         return cfg;
6170     },
6171     /**
6172     * sets the active Navigation item
6173     * @param {Roo.bootstrap.nav.Item} the new current navitem
6174     */
6175     setActiveItem : function(item)
6176     {
6177         var prev = false;
6178         Roo.each(this.navItems, function(v){
6179             if (v == item) {
6180                 return ;
6181             }
6182             if (v.isActive()) {
6183                 v.setActive(false, true);
6184                 prev = v;
6185                 
6186             }
6187             
6188         });
6189
6190         item.setActive(true, true);
6191         this.fireEvent('changed', this, item, prev);
6192         
6193         
6194     },
6195     /**
6196     * gets the active Navigation item
6197     * @return {Roo.bootstrap.nav.Item} the current navitem
6198     */
6199     getActive : function()
6200     {
6201         
6202         var prev = false;
6203         Roo.each(this.navItems, function(v){
6204             
6205             if (v.isActive()) {
6206                 prev = v;
6207                 
6208             }
6209             
6210         });
6211         return prev;
6212     },
6213     
6214     indexOfNav : function()
6215     {
6216         
6217         var prev = false;
6218         Roo.each(this.navItems, function(v,i){
6219             
6220             if (v.isActive()) {
6221                 prev = i;
6222                 
6223             }
6224             
6225         });
6226         return prev;
6227     },
6228     /**
6229     * adds a Navigation item
6230     * @param {Roo.bootstrap.nav.Item} the navitem to add
6231     */
6232     addItem : function(cfg)
6233     {
6234         if (this.form && Roo.bootstrap.version == 4) {
6235             cfg.tag = 'div';
6236         }
6237         var cn = new Roo.bootstrap.nav.Item(cfg);
6238         this.register(cn);
6239         cn.parentId = this.id;
6240         cn.onRender(this.el, null);
6241         return cn;
6242     },
6243     /**
6244     * register a Navigation item
6245     * @param {Roo.bootstrap.nav.Item} the navitem to add
6246     */
6247     register : function(item)
6248     {
6249         this.navItems.push( item);
6250         item.navId = this.navId;
6251     
6252     },
6253     
6254     /**
6255     * clear all the Navigation item
6256     */
6257    
6258     clearAll : function()
6259     {
6260         this.navItems = [];
6261         this.el.dom.innerHTML = '';
6262     },
6263     
6264     getNavItem: function(tabId)
6265     {
6266         var ret = false;
6267         Roo.each(this.navItems, function(e) {
6268             if (e.tabId == tabId) {
6269                ret =  e;
6270                return false;
6271             }
6272             return true;
6273             
6274         });
6275         return ret;
6276     },
6277     
6278     setActiveNext : function()
6279     {
6280         var i = this.indexOfNav(this.getActive());
6281         if (i > this.navItems.length) {
6282             return;
6283         }
6284         this.setActiveItem(this.navItems[i+1]);
6285     },
6286     setActivePrev : function()
6287     {
6288         var i = this.indexOfNav(this.getActive());
6289         if (i  < 1) {
6290             return;
6291         }
6292         this.setActiveItem(this.navItems[i-1]);
6293     },
6294     clearWasActive : function(except) {
6295         Roo.each(this.navItems, function(e) {
6296             if (e.tabId != except.tabId && e.was_active) {
6297                e.was_active = false;
6298                return false;
6299             }
6300             return true;
6301             
6302         });
6303     },
6304     getWasActive : function ()
6305     {
6306         var r = false;
6307         Roo.each(this.navItems, function(e) {
6308             if (e.was_active) {
6309                r = e;
6310                return false;
6311             }
6312             return true;
6313             
6314         });
6315         return r;
6316     }
6317     
6318     
6319 });
6320
6321  
6322 Roo.apply(Roo.bootstrap.nav.Group, {
6323     
6324     groups: {},
6325      /**
6326     * register a Navigation Group
6327     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6328     */
6329     register : function(navgrp)
6330     {
6331         this.groups[navgrp.navId] = navgrp;
6332         
6333     },
6334     /**
6335     * fetch a Navigation Group based on the navigation ID
6336     * @param {string} the navgroup to add
6337     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6338     */
6339     get: function(navId) {
6340         if (typeof(this.groups[navId]) == 'undefined') {
6341             return false;
6342             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6343         }
6344         return this.groups[navId] ;
6345     }
6346     
6347     
6348     
6349 });
6350
6351  /**
6352  * @class Roo.bootstrap.nav.Item
6353  * @extends Roo.bootstrap.Component
6354  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6355  * @licence LGPL
6356  * Bootstrap Navbar.NavItem class
6357  * 
6358  * @cfg {String} href  link to
6359  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6360  * @cfg {Boolean} button_outline show and outlined button
6361  * @cfg {String} html content of button
6362  * @cfg {String} badge text inside badge
6363  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6364  * @cfg {String} glyphicon DEPRICATED - use fa
6365  * @cfg {String} icon DEPRICATED - use fa
6366  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6367  * @cfg {Boolean} active Is item active
6368  * @cfg {Boolean} disabled Is item disabled
6369  * @cfg {String} linkcls  Link Class
6370  * @cfg {Boolean} preventDefault (true | false) default false
6371  * @cfg {String} tabId the tab that this item activates.
6372  * @cfg {String} tagtype (a|span) render as a href or span?
6373  * @cfg {Boolean} animateRef (true|false) link to element default false  
6374  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6375   
6376  * @constructor
6377  * Create a new Navbar Item
6378  * @param {Object} config The config object
6379  */
6380 Roo.bootstrap.nav.Item = function(config){
6381     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6382     this.addEvents({
6383         // raw events
6384         /**
6385          * @event click
6386          * The raw click event for the entire grid.
6387          * @param {Roo.EventObject} e
6388          */
6389         "click" : true,
6390          /**
6391             * @event changed
6392             * Fires when the active item active state changes
6393             * @param {Roo.bootstrap.nav.Item} this
6394             * @param {boolean} state the new state
6395              
6396          */
6397         'changed': true,
6398         /**
6399             * @event scrollto
6400             * Fires when scroll to element
6401             * @param {Roo.bootstrap.nav.Item} this
6402             * @param {Object} options
6403             * @param {Roo.EventObject} e
6404              
6405          */
6406         'scrollto': true
6407     });
6408    
6409 };
6410
6411 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6412     
6413     href: false,
6414     html: '',
6415     badge: '',
6416     icon: false,
6417     fa : false,
6418     glyphicon: false,
6419     active: false,
6420     preventDefault : false,
6421     tabId : false,
6422     tagtype : 'a',
6423     tag: 'li',
6424     disabled : false,
6425     animateRef : false,
6426     was_active : false,
6427     button_weight : '',
6428     button_outline : false,
6429     linkcls : '',
6430     navLink: false,
6431     
6432     getAutoCreate : function(){
6433          
6434         var cfg = {
6435             tag: this.tag,
6436             cls: 'nav-item'
6437         };
6438         
6439         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6440         
6441         if (this.active) {
6442             cfg.cls +=  ' active' ;
6443         }
6444         if (this.disabled) {
6445             cfg.cls += ' disabled';
6446         }
6447         
6448         // BS4 only?
6449         if (this.button_weight.length) {
6450             cfg.tag = this.href ? 'a' : 'button';
6451             cfg.html = this.html || '';
6452             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6453             if (this.href) {
6454                 cfg.href = this.href;
6455             }
6456             if (this.fa) {
6457                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6458             } else {
6459                 cfg.cls += " nav-html";
6460             }
6461             
6462             // menu .. should add dropdown-menu class - so no need for carat..
6463             
6464             if (this.badge !== '') {
6465                  
6466                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6467             }
6468             return cfg;
6469         }
6470         
6471         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6472             cfg.cn = [
6473                 {
6474                     tag: this.tagtype,
6475                     href : this.href || "#",
6476                     html: this.html || '',
6477                     cls : ''
6478                 }
6479             ];
6480             if (this.tagtype == 'a') {
6481                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6482         
6483             }
6484             if (this.icon) {
6485                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6486             } else  if (this.fa) {
6487                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6488             } else if(this.glyphicon) {
6489                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6490             } else {
6491                 cfg.cn[0].cls += " nav-html";
6492             }
6493             
6494             if (this.menu) {
6495                 cfg.cn[0].html += " <span class='caret'></span>";
6496              
6497             }
6498             
6499             if (this.badge !== '') {
6500                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6501             }
6502         }
6503         
6504         
6505         
6506         return cfg;
6507     },
6508     onRender : function(ct, position)
6509     {
6510        // Roo.log("Call onRender: " + this.xtype);
6511         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6512             this.tag = 'div';
6513         }
6514         
6515         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6516         this.navLink = this.el.select('.nav-link',true).first();
6517         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6518         return ret;
6519     },
6520       
6521     
6522     initEvents: function() 
6523     {
6524         if (typeof (this.menu) != 'undefined') {
6525             this.menu.parentType = this.xtype;
6526             this.menu.triggerEl = this.el;
6527             this.menu = this.addxtype(Roo.apply({}, this.menu));
6528         }
6529         
6530         this.el.on('click', this.onClick, this);
6531         
6532         //if(this.tagtype == 'span'){
6533         //    this.el.select('span',true).on('click', this.onClick, this);
6534         //}
6535        
6536         // at this point parent should be available..
6537         this.parent().register(this);
6538     },
6539     
6540     onClick : function(e)
6541     {
6542         if (e.getTarget('.dropdown-menu-item')) {
6543             // did you click on a menu itemm.... - then don't trigger onclick..
6544             return;
6545         }
6546         
6547         if(
6548                 this.preventDefault || 
6549                 this.href == '#' 
6550         ){
6551             Roo.log("NavItem - prevent Default?");
6552             e.preventDefault();
6553         }
6554         
6555         if (this.disabled) {
6556             return;
6557         }
6558         
6559         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6560         if (tg && tg.transition) {
6561             Roo.log("waiting for the transitionend");
6562             return;
6563         }
6564         
6565         
6566         
6567         //Roo.log("fire event clicked");
6568         if(this.fireEvent('click', this, e) === false){
6569             return;
6570         };
6571         
6572         if(this.tagtype == 'span'){
6573             return;
6574         }
6575         
6576         //Roo.log(this.href);
6577         var ael = this.el.select('a',true).first();
6578         //Roo.log(ael);
6579         
6580         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6581             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6582             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6583                 return; // ignore... - it's a 'hash' to another page.
6584             }
6585             Roo.log("NavItem - prevent Default?");
6586             e.preventDefault();
6587             this.scrollToElement(e);
6588         }
6589         
6590         
6591         var p =  this.parent();
6592    
6593         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6594             if (typeof(p.setActiveItem) !== 'undefined') {
6595                 p.setActiveItem(this);
6596             }
6597         }
6598         
6599         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6600         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6601             // remove the collapsed menu expand...
6602             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6603         }
6604     },
6605     
6606     isActive: function () {
6607         return this.active
6608     },
6609     setActive : function(state, fire, is_was_active)
6610     {
6611         if (this.active && !state && this.navId) {
6612             this.was_active = true;
6613             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6614             if (nv) {
6615                 nv.clearWasActive(this);
6616             }
6617             
6618         }
6619         this.active = state;
6620         
6621         if (!state ) {
6622             this.el.removeClass('active');
6623             this.navLink ? this.navLink.removeClass('active') : false;
6624         } else if (!this.el.hasClass('active')) {
6625             
6626             this.el.addClass('active');
6627             if (Roo.bootstrap.version == 4 && this.navLink ) {
6628                 this.navLink.addClass('active');
6629             }
6630             
6631         }
6632         if (fire) {
6633             this.fireEvent('changed', this, state);
6634         }
6635         
6636         // show a panel if it's registered and related..
6637         
6638         if (!this.navId || !this.tabId || !state || is_was_active) {
6639             return;
6640         }
6641         
6642         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6643         if (!tg) {
6644             return;
6645         }
6646         var pan = tg.getPanelByName(this.tabId);
6647         if (!pan) {
6648             return;
6649         }
6650         // if we can not flip to new panel - go back to old nav highlight..
6651         if (false == tg.showPanel(pan)) {
6652             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6653             if (nv) {
6654                 var onav = nv.getWasActive();
6655                 if (onav) {
6656                     onav.setActive(true, false, true);
6657                 }
6658             }
6659             
6660         }
6661         
6662         
6663         
6664     },
6665      // this should not be here...
6666     setDisabled : function(state)
6667     {
6668         this.disabled = state;
6669         if (!state ) {
6670             this.el.removeClass('disabled');
6671         } else if (!this.el.hasClass('disabled')) {
6672             this.el.addClass('disabled');
6673         }
6674         
6675     },
6676     
6677     /**
6678      * Fetch the element to display the tooltip on.
6679      * @return {Roo.Element} defaults to this.el
6680      */
6681     tooltipEl : function()
6682     {
6683         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6684     },
6685     
6686     scrollToElement : function(e)
6687     {
6688         var c = document.body;
6689         
6690         /*
6691          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6692          */
6693         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6694             c = document.documentElement;
6695         }
6696         
6697         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6698         
6699         if(!target){
6700             return;
6701         }
6702
6703         var o = target.calcOffsetsTo(c);
6704         
6705         var options = {
6706             target : target,
6707             value : o[1]
6708         };
6709         
6710         this.fireEvent('scrollto', this, options, e);
6711         
6712         Roo.get(c).scrollTo('top', options.value, true);
6713         
6714         return;
6715     },
6716     /**
6717      * Set the HTML (text content) of the item
6718      * @param {string} html  content for the nav item
6719      */
6720     setHtml : function(html)
6721     {
6722         this.html = html;
6723         this.htmlEl.dom.innerHTML = html;
6724         
6725     } 
6726 });
6727  
6728
6729  /*
6730  * - LGPL
6731  *
6732  * sidebar item
6733  *
6734  *  li
6735  *    <span> icon </span>
6736  *    <span> text </span>
6737  *    <span>badge </span>
6738  */
6739
6740 /**
6741  * @class Roo.bootstrap.nav.SidebarItem
6742  * @extends Roo.bootstrap.nav.Item
6743  * Bootstrap Navbar.NavSidebarItem class
6744  * 
6745  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6746  * {Boolean} open is the menu open
6747  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6748  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6749  * {String} buttonSize (sm|md|lg)the extra classes for the button
6750  * {Boolean} showArrow show arrow next to the text (default true)
6751  * @constructor
6752  * Create a new Navbar Button
6753  * @param {Object} config The config object
6754  */
6755 Roo.bootstrap.nav.SidebarItem = function(config){
6756     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6757     this.addEvents({
6758         // raw events
6759         /**
6760          * @event click
6761          * The raw click event for the entire grid.
6762          * @param {Roo.EventObject} e
6763          */
6764         "click" : true,
6765          /**
6766             * @event changed
6767             * Fires when the active item active state changes
6768             * @param {Roo.bootstrap.nav.SidebarItem} this
6769             * @param {boolean} state the new state
6770              
6771          */
6772         'changed': true
6773     });
6774    
6775 };
6776
6777 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6778     
6779     badgeWeight : 'default',
6780     
6781     open: false,
6782     
6783     buttonView : false,
6784     
6785     buttonWeight : 'default',
6786     
6787     buttonSize : 'md',
6788     
6789     showArrow : true,
6790     
6791     getAutoCreate : function(){
6792         
6793         
6794         var a = {
6795                 tag: 'a',
6796                 href : this.href || '#',
6797                 cls: '',
6798                 html : '',
6799                 cn : []
6800         };
6801         
6802         if(this.buttonView){
6803             a = {
6804                 tag: 'button',
6805                 href : this.href || '#',
6806                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6807                 html : this.html,
6808                 cn : []
6809             };
6810         }
6811         
6812         var cfg = {
6813             tag: 'li',
6814             cls: '',
6815             cn: [ a ]
6816         };
6817         
6818         if (this.active) {
6819             cfg.cls += ' active';
6820         }
6821         
6822         if (this.disabled) {
6823             cfg.cls += ' disabled';
6824         }
6825         if (this.open) {
6826             cfg.cls += ' open x-open';
6827         }
6828         // left icon..
6829         if (this.glyphicon || this.icon) {
6830             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6831             a.cn.push({ tag : 'i', cls : c }) ;
6832         }
6833         
6834         if(!this.buttonView){
6835             var span = {
6836                 tag: 'span',
6837                 html : this.html || ''
6838             };
6839
6840             a.cn.push(span);
6841             
6842         }
6843         
6844         if (this.badge !== '') {
6845             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6846         }
6847         
6848         if (this.menu) {
6849             
6850             if(this.showArrow){
6851                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6852             }
6853             
6854             a.cls += ' dropdown-toggle treeview' ;
6855         }
6856         
6857         return cfg;
6858     },
6859     
6860     initEvents : function()
6861     { 
6862         if (typeof (this.menu) != 'undefined') {
6863             this.menu.parentType = this.xtype;
6864             this.menu.triggerEl = this.el;
6865             this.menu = this.addxtype(Roo.apply({}, this.menu));
6866         }
6867         
6868         this.el.on('click', this.onClick, this);
6869         
6870         if(this.badge !== ''){
6871             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6872         }
6873         
6874     },
6875     
6876     onClick : function(e)
6877     {
6878         if(this.disabled){
6879             e.preventDefault();
6880             return;
6881         }
6882         
6883         if(this.preventDefault){
6884             e.preventDefault();
6885         }
6886         
6887         this.fireEvent('click', this, e);
6888     },
6889     
6890     disable : function()
6891     {
6892         this.setDisabled(true);
6893     },
6894     
6895     enable : function()
6896     {
6897         this.setDisabled(false);
6898     },
6899     
6900     setDisabled : function(state)
6901     {
6902         if(this.disabled == state){
6903             return;
6904         }
6905         
6906         this.disabled = state;
6907         
6908         if (state) {
6909             this.el.addClass('disabled');
6910             return;
6911         }
6912         
6913         this.el.removeClass('disabled');
6914         
6915         return;
6916     },
6917     
6918     setActive : function(state)
6919     {
6920         if(this.active == state){
6921             return;
6922         }
6923         
6924         this.active = state;
6925         
6926         if (state) {
6927             this.el.addClass('active');
6928             return;
6929         }
6930         
6931         this.el.removeClass('active');
6932         
6933         return;
6934     },
6935     
6936     isActive: function () 
6937     {
6938         return this.active;
6939     },
6940     
6941     setBadge : function(str)
6942     {
6943         if(!this.badgeEl){
6944             return;
6945         }
6946         
6947         this.badgeEl.dom.innerHTML = str;
6948     }
6949     
6950    
6951      
6952  
6953 });
6954  
6955
6956  /*
6957  * - LGPL
6958  *
6959  * nav progress bar
6960  * 
6961  */
6962
6963 /**
6964  * @class Roo.bootstrap.nav.ProgressBar
6965  * @extends Roo.bootstrap.Component
6966  * @children Roo.bootstrap.nav.ProgressBarItem
6967  * Bootstrap NavProgressBar class
6968  * 
6969  * @constructor
6970  * Create a new nav progress bar - a bar indicating step along a process
6971  * @param {Object} config The config object
6972  */
6973
6974 Roo.bootstrap.nav.ProgressBar = function(config){
6975     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6976
6977     this.bullets = this.bullets || [];
6978    
6979 //    Roo.bootstrap.nav.ProgressBar.register(this);
6980      this.addEvents({
6981         /**
6982              * @event changed
6983              * Fires when the active item changes
6984              * @param {Roo.bootstrap.nav.ProgressBar} this
6985              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6986              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6987          */
6988         'changed': true
6989      });
6990     
6991 };
6992
6993 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6994     /**
6995      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6996      * Bullets for the Nav Progress bar for the toolbar
6997      */
6998     bullets : [],
6999     barItems : [],
7000     
7001     getAutoCreate : function()
7002     {
7003         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7004         
7005         cfg = {
7006             tag : 'div',
7007             cls : 'roo-navigation-bar-group',
7008             cn : [
7009                 {
7010                     tag : 'div',
7011                     cls : 'roo-navigation-top-bar'
7012                 },
7013                 {
7014                     tag : 'div',
7015                     cls : 'roo-navigation-bullets-bar',
7016                     cn : [
7017                         {
7018                             tag : 'ul',
7019                             cls : 'roo-navigation-bar'
7020                         }
7021                     ]
7022                 },
7023                 
7024                 {
7025                     tag : 'div',
7026                     cls : 'roo-navigation-bottom-bar'
7027                 }
7028             ]
7029             
7030         };
7031         
7032         return cfg;
7033         
7034     },
7035     
7036     initEvents: function() 
7037     {
7038         
7039     },
7040     
7041     onRender : function(ct, position) 
7042     {
7043         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7044         
7045         if(this.bullets.length){
7046             Roo.each(this.bullets, function(b){
7047                this.addItem(b);
7048             }, this);
7049         }
7050         
7051         this.format();
7052         
7053     },
7054     
7055     addItem : function(cfg)
7056     {
7057         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7058         
7059         item.parentId = this.id;
7060         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7061         
7062         if(cfg.html){
7063             var top = new Roo.bootstrap.Element({
7064                 tag : 'div',
7065                 cls : 'roo-navigation-bar-text'
7066             });
7067             
7068             var bottom = new Roo.bootstrap.Element({
7069                 tag : 'div',
7070                 cls : 'roo-navigation-bar-text'
7071             });
7072             
7073             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7074             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7075             
7076             var topText = new Roo.bootstrap.Element({
7077                 tag : 'span',
7078                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7079             });
7080             
7081             var bottomText = new Roo.bootstrap.Element({
7082                 tag : 'span',
7083                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7084             });
7085             
7086             topText.onRender(top.el, null);
7087             bottomText.onRender(bottom.el, null);
7088             
7089             item.topEl = top;
7090             item.bottomEl = bottom;
7091         }
7092         
7093         this.barItems.push(item);
7094         
7095         return item;
7096     },
7097     
7098     getActive : function()
7099     {
7100         var active = false;
7101         
7102         Roo.each(this.barItems, function(v){
7103             
7104             if (!v.isActive()) {
7105                 return;
7106             }
7107             
7108             active = v;
7109             return false;
7110             
7111         });
7112         
7113         return active;
7114     },
7115     
7116     setActiveItem : function(item)
7117     {
7118         var prev = false;
7119         
7120         Roo.each(this.barItems, function(v){
7121             if (v.rid == item.rid) {
7122                 return ;
7123             }
7124             
7125             if (v.isActive()) {
7126                 v.setActive(false);
7127                 prev = v;
7128             }
7129         });
7130
7131         item.setActive(true);
7132         
7133         this.fireEvent('changed', this, item, prev);
7134     },
7135     
7136     getBarItem: function(rid)
7137     {
7138         var ret = false;
7139         
7140         Roo.each(this.barItems, function(e) {
7141             if (e.rid != rid) {
7142                 return;
7143             }
7144             
7145             ret =  e;
7146             return false;
7147         });
7148         
7149         return ret;
7150     },
7151     
7152     indexOfItem : function(item)
7153     {
7154         var index = false;
7155         
7156         Roo.each(this.barItems, function(v, i){
7157             
7158             if (v.rid != item.rid) {
7159                 return;
7160             }
7161             
7162             index = i;
7163             return false
7164         });
7165         
7166         return index;
7167     },
7168     
7169     setActiveNext : function()
7170     {
7171         var i = this.indexOfItem(this.getActive());
7172         
7173         if (i > this.barItems.length) {
7174             return;
7175         }
7176         
7177         this.setActiveItem(this.barItems[i+1]);
7178     },
7179     
7180     setActivePrev : function()
7181     {
7182         var i = this.indexOfItem(this.getActive());
7183         
7184         if (i  < 1) {
7185             return;
7186         }
7187         
7188         this.setActiveItem(this.barItems[i-1]);
7189     },
7190     
7191     format : function()
7192     {
7193         if(!this.barItems.length){
7194             return;
7195         }
7196      
7197         var width = 100 / this.barItems.length;
7198         
7199         Roo.each(this.barItems, function(i){
7200             i.el.setStyle('width', width + '%');
7201             i.topEl.el.setStyle('width', width + '%');
7202             i.bottomEl.el.setStyle('width', width + '%');
7203         }, this);
7204         
7205     }
7206     
7207 });
7208 /*
7209  * - LGPL
7210  *
7211  * Nav Progress Item
7212  * 
7213  */
7214
7215 /**
7216  * @class Roo.bootstrap.nav.ProgressBarItem
7217  * @extends Roo.bootstrap.Component
7218  * Bootstrap NavProgressBarItem class
7219  * @cfg {String} rid the reference id
7220  * @cfg {Boolean} active (true|false) Is item active default false
7221  * @cfg {Boolean} disabled (true|false) Is item active default false
7222  * @cfg {String} html
7223  * @cfg {String} position (top|bottom) text position default bottom
7224  * @cfg {String} icon show icon instead of number
7225  * 
7226  * @constructor
7227  * Create a new NavProgressBarItem
7228  * @param {Object} config The config object
7229  */
7230 Roo.bootstrap.nav.ProgressBarItem = function(config){
7231     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7232     this.addEvents({
7233         // raw events
7234         /**
7235          * @event click
7236          * The raw click event for the entire grid.
7237          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7238          * @param {Roo.EventObject} e
7239          */
7240         "click" : true
7241     });
7242    
7243 };
7244
7245 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7246     
7247     rid : '',
7248     active : false,
7249     disabled : false,
7250     html : '',
7251     position : 'bottom',
7252     icon : false,
7253     
7254     getAutoCreate : function()
7255     {
7256         var iconCls = 'roo-navigation-bar-item-icon';
7257         
7258         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7259         
7260         var cfg = {
7261             tag: 'li',
7262             cls: 'roo-navigation-bar-item',
7263             cn : [
7264                 {
7265                     tag : 'i',
7266                     cls : iconCls
7267                 }
7268             ]
7269         };
7270         
7271         if(this.active){
7272             cfg.cls += ' active';
7273         }
7274         if(this.disabled){
7275             cfg.cls += ' disabled';
7276         }
7277         
7278         return cfg;
7279     },
7280     
7281     disable : function()
7282     {
7283         this.setDisabled(true);
7284     },
7285     
7286     enable : function()
7287     {
7288         this.setDisabled(false);
7289     },
7290     
7291     initEvents: function() 
7292     {
7293         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7294         
7295         this.iconEl.on('click', this.onClick, this);
7296     },
7297     
7298     onClick : function(e)
7299     {
7300         e.preventDefault();
7301         
7302         if(this.disabled){
7303             return;
7304         }
7305         
7306         if(this.fireEvent('click', this, e) === false){
7307             return;
7308         };
7309         
7310         this.parent().setActiveItem(this);
7311     },
7312     
7313     isActive: function () 
7314     {
7315         return this.active;
7316     },
7317     
7318     setActive : function(state)
7319     {
7320         if(this.active == state){
7321             return;
7322         }
7323         
7324         this.active = state;
7325         
7326         if (state) {
7327             this.el.addClass('active');
7328             return;
7329         }
7330         
7331         this.el.removeClass('active');
7332         
7333         return;
7334     },
7335     
7336     setDisabled : function(state)
7337     {
7338         if(this.disabled == state){
7339             return;
7340         }
7341         
7342         this.disabled = state;
7343         
7344         if (state) {
7345             this.el.addClass('disabled');
7346             return;
7347         }
7348         
7349         this.el.removeClass('disabled');
7350     },
7351     
7352     tooltipEl : function()
7353     {
7354         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7355     }
7356 });
7357  
7358
7359  // depricated.
7360 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
7361 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
7362 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
7363 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
7364
7365 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
7366 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
7367
7368 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
7369 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
7370
7371 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;/*
7372  * - LGPL
7373  *
7374  *  Breadcrumb Nav
7375  * 
7376  */
7377 Roo.namespace('Roo.bootstrap.breadcrumb');
7378
7379
7380 /**
7381  * @class Roo.bootstrap.breadcrumb.Nav
7382  * @extends Roo.bootstrap.Component
7383  * Bootstrap Breadcrumb Nav Class
7384  *  
7385  * @children Roo.bootstrap.breadcrumb.Item
7386  * 
7387  * @constructor
7388  * Create a new breadcrumb.Nav
7389  * @param {Object} config The config object
7390  */
7391
7392
7393 Roo.bootstrap.breadcrumb.Nav = function(config){
7394     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7395     
7396     
7397 };
7398
7399 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7400     
7401     getAutoCreate : function()
7402     {
7403
7404         var cfg = {
7405             tag: 'nav',
7406             cn : [
7407                 {
7408                     tag : 'ol',
7409                     cls : 'breadcrumb'
7410                 }
7411             ]
7412             
7413         };
7414           
7415         return cfg;
7416     },
7417     
7418     initEvents: function()
7419     {
7420         this.olEl = this.el.select('ol',true).first();    
7421     },
7422     getChildContainer : function()
7423     {
7424         return this.olEl;  
7425     }
7426     
7427 });
7428
7429  /*
7430  * - LGPL
7431  *
7432  *  Breadcrumb Item
7433  * 
7434  */
7435
7436
7437 /**
7438  * @class Roo.bootstrap.breadcrumb.Nav
7439  * @extends Roo.bootstrap.Component
7440  * @children Roo.bootstrap.Component
7441  * Bootstrap Breadcrumb Nav Class
7442  *  
7443  * 
7444  * @cfg {String} html the content of the link.
7445  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7446  * @cfg {Boolean} active is it active
7447
7448  * 
7449  * @constructor
7450  * Create a new breadcrumb.Nav
7451  * @param {Object} config The config object
7452  */
7453
7454 Roo.bootstrap.breadcrumb.Item = function(config){
7455     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7456     this.addEvents({
7457         // img events
7458         /**
7459          * @event click
7460          * The img click event for the img.
7461          * @param {Roo.EventObject} e
7462          */
7463         "click" : true
7464     });
7465     
7466 };
7467
7468 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7469     
7470     href: false,
7471     html : '',
7472     
7473     getAutoCreate : function()
7474     {
7475
7476         var cfg = {
7477             tag: 'li',
7478             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7479         };
7480         if (this.href !== false) {
7481             cfg.cn = [{
7482                 tag : 'a',
7483                 href : this.href,
7484                 html : this.html
7485             }];
7486         } else {
7487             cfg.html = this.html;
7488         }
7489         
7490         return cfg;
7491     },
7492     
7493     initEvents: function()
7494     {
7495         if (this.href) {
7496             this.el.select('a', true).first().on('click',this.onClick, this)
7497         }
7498         
7499     },
7500     onClick : function(e)
7501     {
7502         e.preventDefault();
7503         this.fireEvent('click',this,  e);
7504     }
7505     
7506 });
7507
7508  /*
7509  * - LGPL
7510  *
7511  * row
7512  * 
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Row
7517  * @extends Roo.bootstrap.Component
7518  * @children Roo.bootstrap.Component
7519  * Bootstrap Row class (contains columns...)
7520  * 
7521  * @constructor
7522  * Create a new Row
7523  * @param {Object} config The config object
7524  */
7525
7526 Roo.bootstrap.Row = function(config){
7527     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7528 };
7529
7530 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7531     
7532     getAutoCreate : function(){
7533        return {
7534             cls: 'row clearfix'
7535        };
7536     }
7537     
7538     
7539 });
7540
7541  
7542
7543  /*
7544  * - LGPL
7545  *
7546  * pagination
7547  * 
7548  */
7549
7550 /**
7551  * @class Roo.bootstrap.Pagination
7552  * @extends Roo.bootstrap.Component
7553  * @children Roo.bootstrap.Pagination
7554  * Bootstrap Pagination class
7555  * 
7556  * @cfg {String} size (xs|sm|md|lg|xl)
7557  * @cfg {Boolean} inverse 
7558  * 
7559  * @constructor
7560  * Create a new Pagination
7561  * @param {Object} config The config object
7562  */
7563
7564 Roo.bootstrap.Pagination = function(config){
7565     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7566 };
7567
7568 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7569     
7570     cls: false,
7571     size: false,
7572     inverse: false,
7573     
7574     getAutoCreate : function(){
7575         var cfg = {
7576             tag: 'ul',
7577                 cls: 'pagination'
7578         };
7579         if (this.inverse) {
7580             cfg.cls += ' inverse';
7581         }
7582         if (this.html) {
7583             cfg.html=this.html;
7584         }
7585         if (this.cls) {
7586             cfg.cls += " " + this.cls;
7587         }
7588         return cfg;
7589     }
7590    
7591 });
7592
7593  
7594
7595  /*
7596  * - LGPL
7597  *
7598  * Pagination item
7599  * 
7600  */
7601
7602
7603 /**
7604  * @class Roo.bootstrap.PaginationItem
7605  * @extends Roo.bootstrap.Component
7606  * Bootstrap PaginationItem class
7607  * @cfg {String} html text
7608  * @cfg {String} href the link
7609  * @cfg {Boolean} preventDefault (true | false) default true
7610  * @cfg {Boolean} active (true | false) default false
7611  * @cfg {Boolean} disabled default false
7612  * 
7613  * 
7614  * @constructor
7615  * Create a new PaginationItem
7616  * @param {Object} config The config object
7617  */
7618
7619
7620 Roo.bootstrap.PaginationItem = function(config){
7621     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7622     this.addEvents({
7623         // raw events
7624         /**
7625          * @event click
7626          * The raw click event for the entire grid.
7627          * @param {Roo.EventObject} e
7628          */
7629         "click" : true
7630     });
7631 };
7632
7633 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7634     
7635     href : false,
7636     html : false,
7637     preventDefault: true,
7638     active : false,
7639     cls : false,
7640     disabled: false,
7641     
7642     getAutoCreate : function(){
7643         var cfg= {
7644             tag: 'li',
7645             cn: [
7646                 {
7647                     tag : 'a',
7648                     href : this.href ? this.href : '#',
7649                     html : this.html ? this.html : ''
7650                 }
7651             ]
7652         };
7653         
7654         if(this.cls){
7655             cfg.cls = this.cls;
7656         }
7657         
7658         if(this.disabled){
7659             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7660         }
7661         
7662         if(this.active){
7663             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7664         }
7665         
7666         return cfg;
7667     },
7668     
7669     initEvents: function() {
7670         
7671         this.el.on('click', this.onClick, this);
7672         
7673     },
7674     onClick : function(e)
7675     {
7676         Roo.log('PaginationItem on click ');
7677         if(this.preventDefault){
7678             e.preventDefault();
7679         }
7680         
7681         if(this.disabled){
7682             return;
7683         }
7684         
7685         this.fireEvent('click', this, e);
7686     }
7687    
7688 });
7689
7690  
7691
7692  /*
7693  * - LGPL
7694  *
7695  * slider
7696  * 
7697  */
7698
7699
7700 /**
7701  * @class Roo.bootstrap.Slider
7702  * @extends Roo.bootstrap.Component
7703  * Bootstrap Slider class
7704  *    
7705  * @constructor
7706  * Create a new Slider
7707  * @param {Object} config The config object
7708  */
7709
7710 Roo.bootstrap.Slider = function(config){
7711     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7712 };
7713
7714 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7715     
7716     getAutoCreate : function(){
7717         
7718         var cfg = {
7719             tag: 'div',
7720             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7721             cn: [
7722                 {
7723                     tag: 'a',
7724                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7725                 }
7726             ]
7727         };
7728         
7729         return cfg;
7730     }
7731    
7732 });
7733
7734  /*
7735  * Based on:
7736  * Ext JS Library 1.1.1
7737  * Copyright(c) 2006-2007, Ext JS, LLC.
7738  *
7739  * Originally Released Under LGPL - original licence link has changed is not relivant.
7740  *
7741  * Fork - LGPL
7742  * <script type="text/javascript">
7743  */
7744  /**
7745  * @extends Roo.dd.DDProxy
7746  * @class Roo.grid.SplitDragZone
7747  * Support for Column Header resizing
7748  * @constructor
7749  * @param {Object} config
7750  */
7751 // private
7752 // This is a support class used internally by the Grid components
7753 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7754     this.grid = grid;
7755     this.view = grid.getView();
7756     this.proxy = this.view.resizeProxy;
7757     Roo.grid.SplitDragZone.superclass.constructor.call(
7758         this,
7759         hd, // ID
7760         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7761         {  // CONFIG
7762             dragElId : Roo.id(this.proxy.dom),
7763             resizeFrame:false
7764         }
7765     );
7766     
7767     this.setHandleElId(Roo.id(hd));
7768     if (hd2 !== false) {
7769         this.setOuterHandleElId(Roo.id(hd2));
7770     }
7771     
7772     this.scroll = false;
7773 };
7774 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7775     fly: Roo.Element.fly,
7776
7777     b4StartDrag : function(x, y){
7778         this.view.headersDisabled = true;
7779         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7780                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7781         );
7782         this.proxy.setHeight(h);
7783         
7784         // for old system colWidth really stored the actual width?
7785         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7786         // which in reality did not work.. - it worked only for fixed sizes
7787         // for resizable we need to use actual sizes.
7788         var w = this.cm.getColumnWidth(this.cellIndex);
7789         if (!this.view.mainWrap) {
7790             // bootstrap.
7791             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7792         }
7793         
7794         
7795         
7796         // this was w-this.grid.minColumnWidth;
7797         // doesnt really make sense? - w = thie curren width or the rendered one?
7798         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7799         this.resetConstraints();
7800         this.setXConstraint(minw, 1000);
7801         this.setYConstraint(0, 0);
7802         this.minX = x - minw;
7803         this.maxX = x + 1000;
7804         this.startPos = x;
7805         if (!this.view.mainWrap) { // this is Bootstrap code..
7806             this.getDragEl().style.display='block';
7807         }
7808         
7809         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7810     },
7811
7812
7813     handleMouseDown : function(e){
7814         ev = Roo.EventObject.setEvent(e);
7815         var t = this.fly(ev.getTarget());
7816         if(t.hasClass("x-grid-split")){
7817             this.cellIndex = this.view.getCellIndex(t.dom);
7818             this.split = t.dom;
7819             this.cm = this.grid.colModel;
7820             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7821                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7822             }
7823         }
7824     },
7825
7826     endDrag : function(e){
7827         this.view.headersDisabled = false;
7828         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7829         var diff = endX - this.startPos;
7830         // 
7831         var w = this.cm.getColumnWidth(this.cellIndex);
7832         if (!this.view.mainWrap) {
7833             w = 0;
7834         }
7835         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7836     },
7837
7838     autoOffset : function(){
7839         this.setDelta(0,0);
7840     }
7841 });/*
7842  * Based on:
7843  * Ext JS Library 1.1.1
7844  * Copyright(c) 2006-2007, Ext JS, LLC.
7845  *
7846  * Originally Released Under LGPL - original licence link has changed is not relivant.
7847  *
7848  * Fork - LGPL
7849  * <script type="text/javascript">
7850  */
7851
7852 /**
7853  * @class Roo.grid.AbstractSelectionModel
7854  * @extends Roo.util.Observable
7855  * @abstract
7856  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7857  * implemented by descendant classes.  This class should not be directly instantiated.
7858  * @constructor
7859  */
7860 Roo.grid.AbstractSelectionModel = function(){
7861     this.locked = false;
7862     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7863 };
7864
7865 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7866     /** @ignore Called by the grid automatically. Do not call directly. */
7867     init : function(grid){
7868         this.grid = grid;
7869         this.initEvents();
7870     },
7871
7872     /**
7873      * Locks the selections.
7874      */
7875     lock : function(){
7876         this.locked = true;
7877     },
7878
7879     /**
7880      * Unlocks the selections.
7881      */
7882     unlock : function(){
7883         this.locked = false;
7884     },
7885
7886     /**
7887      * Returns true if the selections are locked.
7888      * @return {Boolean}
7889      */
7890     isLocked : function(){
7891         return this.locked;
7892     }
7893 });/*
7894  * Based on:
7895  * Ext JS Library 1.1.1
7896  * Copyright(c) 2006-2007, Ext JS, LLC.
7897  *
7898  * Originally Released Under LGPL - original licence link has changed is not relivant.
7899  *
7900  * Fork - LGPL
7901  * <script type="text/javascript">
7902  */
7903 /**
7904  * @extends Roo.grid.AbstractSelectionModel
7905  * @class Roo.grid.RowSelectionModel
7906  * The default SelectionModel used by {@link Roo.grid.Grid}.
7907  * It supports multiple selections and keyboard selection/navigation. 
7908  * @constructor
7909  * @param {Object} config
7910  */
7911 Roo.grid.RowSelectionModel = function(config){
7912     Roo.apply(this, config);
7913     this.selections = new Roo.util.MixedCollection(false, function(o){
7914         return o.id;
7915     });
7916
7917     this.last = false;
7918     this.lastActive = false;
7919
7920     this.addEvents({
7921         /**
7922         * @event selectionchange
7923         * Fires when the selection changes
7924         * @param {SelectionModel} this
7925         */
7926        "selectionchange" : true,
7927        /**
7928         * @event afterselectionchange
7929         * Fires after the selection changes (eg. by key press or clicking)
7930         * @param {SelectionModel} this
7931         */
7932        "afterselectionchange" : true,
7933        /**
7934         * @event beforerowselect
7935         * Fires when a row is selected being selected, return false to cancel.
7936         * @param {SelectionModel} this
7937         * @param {Number} rowIndex The selected index
7938         * @param {Boolean} keepExisting False if other selections will be cleared
7939         */
7940        "beforerowselect" : true,
7941        /**
7942         * @event rowselect
7943         * Fires when a row is selected.
7944         * @param {SelectionModel} this
7945         * @param {Number} rowIndex The selected index
7946         * @param {Roo.data.Record} r The record
7947         */
7948        "rowselect" : true,
7949        /**
7950         * @event rowdeselect
7951         * Fires when a row is deselected.
7952         * @param {SelectionModel} this
7953         * @param {Number} rowIndex The selected index
7954         */
7955         "rowdeselect" : true
7956     });
7957     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7958     this.locked = false;
7959 };
7960
7961 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7962     /**
7963      * @cfg {Boolean} singleSelect
7964      * True to allow selection of only one row at a time (defaults to false)
7965      */
7966     singleSelect : false,
7967
7968     // private
7969     initEvents : function(){
7970
7971         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7972             this.grid.on("mousedown", this.handleMouseDown, this);
7973         }else{ // allow click to work like normal
7974             this.grid.on("rowclick", this.handleDragableRowClick, this);
7975         }
7976         // bootstrap does not have a view..
7977         var view = this.grid.view ? this.grid.view : this.grid;
7978         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7979             "up" : function(e){
7980                 if(!e.shiftKey){
7981                     this.selectPrevious(e.shiftKey);
7982                 }else if(this.last !== false && this.lastActive !== false){
7983                     var last = this.last;
7984                     this.selectRange(this.last,  this.lastActive-1);
7985                     view.focusRow(this.lastActive);
7986                     if(last !== false){
7987                         this.last = last;
7988                     }
7989                 }else{
7990                     this.selectFirstRow();
7991                 }
7992                 this.fireEvent("afterselectionchange", this);
7993             },
7994             "down" : function(e){
7995                 if(!e.shiftKey){
7996                     this.selectNext(e.shiftKey);
7997                 }else if(this.last !== false && this.lastActive !== false){
7998                     var last = this.last;
7999                     this.selectRange(this.last,  this.lastActive+1);
8000                     view.focusRow(this.lastActive);
8001                     if(last !== false){
8002                         this.last = last;
8003                     }
8004                 }else{
8005                     this.selectFirstRow();
8006                 }
8007                 this.fireEvent("afterselectionchange", this);
8008             },
8009             scope: this
8010         });
8011
8012          
8013         view.on("refresh", this.onRefresh, this);
8014         view.on("rowupdated", this.onRowUpdated, this);
8015         view.on("rowremoved", this.onRemove, this);
8016     },
8017
8018     // private
8019     onRefresh : function(){
8020         var ds = this.grid.ds, i, v = this.grid.view;
8021         var s = this.selections;
8022         s.each(function(r){
8023             if((i = ds.indexOfId(r.id)) != -1){
8024                 v.onRowSelect(i);
8025                 s.add(ds.getAt(i)); // updating the selection relate data
8026             }else{
8027                 s.remove(r);
8028             }
8029         });
8030     },
8031
8032     // private
8033     onRemove : function(v, index, r){
8034         this.selections.remove(r);
8035     },
8036
8037     // private
8038     onRowUpdated : function(v, index, r){
8039         if(this.isSelected(r)){
8040             v.onRowSelect(index);
8041         }
8042     },
8043
8044     /**
8045      * Select records.
8046      * @param {Array} records The records to select
8047      * @param {Boolean} keepExisting (optional) True to keep existing selections
8048      */
8049     selectRecords : function(records, keepExisting){
8050         if(!keepExisting){
8051             this.clearSelections();
8052         }
8053         var ds = this.grid.ds;
8054         for(var i = 0, len = records.length; i < len; i++){
8055             this.selectRow(ds.indexOf(records[i]), true);
8056         }
8057     },
8058
8059     /**
8060      * Gets the number of selected rows.
8061      * @return {Number}
8062      */
8063     getCount : function(){
8064         return this.selections.length;
8065     },
8066
8067     /**
8068      * Selects the first row in the grid.
8069      */
8070     selectFirstRow : function(){
8071         this.selectRow(0);
8072     },
8073
8074     /**
8075      * Select the last row.
8076      * @param {Boolean} keepExisting (optional) True to keep existing selections
8077      */
8078     selectLastRow : function(keepExisting){
8079         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8080     },
8081
8082     /**
8083      * Selects the row immediately following the last selected row.
8084      * @param {Boolean} keepExisting (optional) True to keep existing selections
8085      */
8086     selectNext : function(keepExisting){
8087         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8088             this.selectRow(this.last+1, keepExisting);
8089             var view = this.grid.view ? this.grid.view : this.grid;
8090             view.focusRow(this.last);
8091         }
8092     },
8093
8094     /**
8095      * Selects the row that precedes the last selected row.
8096      * @param {Boolean} keepExisting (optional) True to keep existing selections
8097      */
8098     selectPrevious : function(keepExisting){
8099         if(this.last){
8100             this.selectRow(this.last-1, keepExisting);
8101             var view = this.grid.view ? this.grid.view : this.grid;
8102             view.focusRow(this.last);
8103         }
8104     },
8105
8106     /**
8107      * Returns the selected records
8108      * @return {Array} Array of selected records
8109      */
8110     getSelections : function(){
8111         return [].concat(this.selections.items);
8112     },
8113
8114     /**
8115      * Returns the first selected record.
8116      * @return {Record}
8117      */
8118     getSelected : function(){
8119         return this.selections.itemAt(0);
8120     },
8121
8122
8123     /**
8124      * Clears all selections.
8125      */
8126     clearSelections : function(fast){
8127         if(this.locked) {
8128             return;
8129         }
8130         if(fast !== true){
8131             var ds = this.grid.ds;
8132             var s = this.selections;
8133             s.each(function(r){
8134                 this.deselectRow(ds.indexOfId(r.id));
8135             }, this);
8136             s.clear();
8137         }else{
8138             this.selections.clear();
8139         }
8140         this.last = false;
8141     },
8142
8143
8144     /**
8145      * Selects all rows.
8146      */
8147     selectAll : function(){
8148         if(this.locked) {
8149             return;
8150         }
8151         this.selections.clear();
8152         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8153             this.selectRow(i, true);
8154         }
8155     },
8156
8157     /**
8158      * Returns True if there is a selection.
8159      * @return {Boolean}
8160      */
8161     hasSelection : function(){
8162         return this.selections.length > 0;
8163     },
8164
8165     /**
8166      * Returns True if the specified row is selected.
8167      * @param {Number/Record} record The record or index of the record to check
8168      * @return {Boolean}
8169      */
8170     isSelected : function(index){
8171         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8172         return (r && this.selections.key(r.id) ? true : false);
8173     },
8174
8175     /**
8176      * Returns True if the specified record id is selected.
8177      * @param {String} id The id of record to check
8178      * @return {Boolean}
8179      */
8180     isIdSelected : function(id){
8181         return (this.selections.key(id) ? true : false);
8182     },
8183
8184     // private
8185     handleMouseDown : function(e, t)
8186     {
8187         var view = this.grid.view ? this.grid.view : this.grid;
8188         var rowIndex;
8189         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8190             return;
8191         };
8192         if(e.shiftKey && this.last !== false){
8193             var last = this.last;
8194             this.selectRange(last, rowIndex, e.ctrlKey);
8195             this.last = last; // reset the last
8196             view.focusRow(rowIndex);
8197         }else{
8198             var isSelected = this.isSelected(rowIndex);
8199             if(e.button !== 0 && isSelected){
8200                 view.focusRow(rowIndex);
8201             }else if(e.ctrlKey && isSelected){
8202                 this.deselectRow(rowIndex);
8203             }else if(!isSelected){
8204                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8205                 view.focusRow(rowIndex);
8206             }
8207         }
8208         this.fireEvent("afterselectionchange", this);
8209     },
8210     // private
8211     handleDragableRowClick :  function(grid, rowIndex, e) 
8212     {
8213         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8214             this.selectRow(rowIndex, false);
8215             var view = this.grid.view ? this.grid.view : this.grid;
8216             view.focusRow(rowIndex);
8217              this.fireEvent("afterselectionchange", this);
8218         }
8219     },
8220     
8221     /**
8222      * Selects multiple rows.
8223      * @param {Array} rows Array of the indexes of the row to select
8224      * @param {Boolean} keepExisting (optional) True to keep existing selections
8225      */
8226     selectRows : function(rows, keepExisting){
8227         if(!keepExisting){
8228             this.clearSelections();
8229         }
8230         for(var i = 0, len = rows.length; i < len; i++){
8231             this.selectRow(rows[i], true);
8232         }
8233     },
8234
8235     /**
8236      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8237      * @param {Number} startRow The index of the first row in the range
8238      * @param {Number} endRow The index of the last row in the range
8239      * @param {Boolean} keepExisting (optional) True to retain existing selections
8240      */
8241     selectRange : function(startRow, endRow, keepExisting){
8242         if(this.locked) {
8243             return;
8244         }
8245         if(!keepExisting){
8246             this.clearSelections();
8247         }
8248         if(startRow <= endRow){
8249             for(var i = startRow; i <= endRow; i++){
8250                 this.selectRow(i, true);
8251             }
8252         }else{
8253             for(var i = startRow; i >= endRow; i--){
8254                 this.selectRow(i, true);
8255             }
8256         }
8257     },
8258
8259     /**
8260      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8261      * @param {Number} startRow The index of the first row in the range
8262      * @param {Number} endRow The index of the last row in the range
8263      */
8264     deselectRange : function(startRow, endRow, preventViewNotify){
8265         if(this.locked) {
8266             return;
8267         }
8268         for(var i = startRow; i <= endRow; i++){
8269             this.deselectRow(i, preventViewNotify);
8270         }
8271     },
8272
8273     /**
8274      * Selects a row.
8275      * @param {Number} row The index of the row to select
8276      * @param {Boolean} keepExisting (optional) True to keep existing selections
8277      */
8278     selectRow : function(index, keepExisting, preventViewNotify){
8279         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8280             return;
8281         }
8282         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8283             if(!keepExisting || this.singleSelect){
8284                 this.clearSelections();
8285             }
8286             var r = this.grid.ds.getAt(index);
8287             this.selections.add(r);
8288             this.last = this.lastActive = index;
8289             if(!preventViewNotify){
8290                 var view = this.grid.view ? this.grid.view : this.grid;
8291                 view.onRowSelect(index);
8292             }
8293             this.fireEvent("rowselect", this, index, r);
8294             this.fireEvent("selectionchange", this);
8295         }
8296     },
8297
8298     /**
8299      * Deselects a row.
8300      * @param {Number} row The index of the row to deselect
8301      */
8302     deselectRow : function(index, preventViewNotify){
8303         if(this.locked) {
8304             return;
8305         }
8306         if(this.last == index){
8307             this.last = false;
8308         }
8309         if(this.lastActive == index){
8310             this.lastActive = false;
8311         }
8312         var r = this.grid.ds.getAt(index);
8313         this.selections.remove(r);
8314         if(!preventViewNotify){
8315             var view = this.grid.view ? this.grid.view : this.grid;
8316             view.onRowDeselect(index);
8317         }
8318         this.fireEvent("rowdeselect", this, index);
8319         this.fireEvent("selectionchange", this);
8320     },
8321
8322     // private
8323     restoreLast : function(){
8324         if(this._last){
8325             this.last = this._last;
8326         }
8327     },
8328
8329     // private
8330     acceptsNav : function(row, col, cm){
8331         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8332     },
8333
8334     // private
8335     onEditorKey : function(field, e){
8336         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8337         if(k == e.TAB){
8338             e.stopEvent();
8339             ed.completeEdit();
8340             if(e.shiftKey){
8341                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8342             }else{
8343                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8344             }
8345         }else if(k == e.ENTER && !e.ctrlKey){
8346             e.stopEvent();
8347             ed.completeEdit();
8348             if(e.shiftKey){
8349                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8350             }else{
8351                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8352             }
8353         }else if(k == e.ESC){
8354             ed.cancelEdit();
8355         }
8356         if(newCell){
8357             g.startEditing(newCell[0], newCell[1]);
8358         }
8359     }
8360 });/*
8361  * Based on:
8362  * Ext JS Library 1.1.1
8363  * Copyright(c) 2006-2007, Ext JS, LLC.
8364  *
8365  * Originally Released Under LGPL - original licence link has changed is not relivant.
8366  *
8367  * Fork - LGPL
8368  * <script type="text/javascript">
8369  */
8370  
8371
8372 /**
8373  * @class Roo.grid.ColumnModel
8374  * @extends Roo.util.Observable
8375  * This is the default implementation of a ColumnModel used by the Grid. It defines
8376  * the columns in the grid.
8377  * <br>Usage:<br>
8378  <pre><code>
8379  var colModel = new Roo.grid.ColumnModel([
8380         {header: "Ticker", width: 60, sortable: true, locked: true},
8381         {header: "Company Name", width: 150, sortable: true},
8382         {header: "Market Cap.", width: 100, sortable: true},
8383         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8384         {header: "Employees", width: 100, sortable: true, resizable: false}
8385  ]);
8386  </code></pre>
8387  * <p>
8388  
8389  * The config options listed for this class are options which may appear in each
8390  * individual column definition.
8391  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8392  * @constructor
8393  * @param {Object} config An Array of column config objects. See this class's
8394  * config objects for details.
8395 */
8396 Roo.grid.ColumnModel = function(config){
8397         /**
8398      * The config passed into the constructor
8399      */
8400     this.config = []; //config;
8401     this.lookup = {};
8402
8403     // if no id, create one
8404     // if the column does not have a dataIndex mapping,
8405     // map it to the order it is in the config
8406     for(var i = 0, len = config.length; i < len; i++){
8407         this.addColumn(config[i]);
8408         
8409     }
8410
8411     /**
8412      * The width of columns which have no width specified (defaults to 100)
8413      * @type Number
8414      */
8415     this.defaultWidth = 100;
8416
8417     /**
8418      * Default sortable of columns which have no sortable specified (defaults to false)
8419      * @type Boolean
8420      */
8421     this.defaultSortable = false;
8422
8423     this.addEvents({
8424         /**
8425              * @event widthchange
8426              * Fires when the width of a column changes.
8427              * @param {ColumnModel} this
8428              * @param {Number} columnIndex The column index
8429              * @param {Number} newWidth The new width
8430              */
8431             "widthchange": true,
8432         /**
8433              * @event headerchange
8434              * Fires when the text of a header changes.
8435              * @param {ColumnModel} this
8436              * @param {Number} columnIndex The column index
8437              * @param {Number} newText The new header text
8438              */
8439             "headerchange": true,
8440         /**
8441              * @event hiddenchange
8442              * Fires when a column is hidden or "unhidden".
8443              * @param {ColumnModel} this
8444              * @param {Number} columnIndex The column index
8445              * @param {Boolean} hidden true if hidden, false otherwise
8446              */
8447             "hiddenchange": true,
8448             /**
8449          * @event columnmoved
8450          * Fires when a column is moved.
8451          * @param {ColumnModel} this
8452          * @param {Number} oldIndex
8453          * @param {Number} newIndex
8454          */
8455         "columnmoved" : true,
8456         /**
8457          * @event columlockchange
8458          * Fires when a column's locked state is changed
8459          * @param {ColumnModel} this
8460          * @param {Number} colIndex
8461          * @param {Boolean} locked true if locked
8462          */
8463         "columnlockchange" : true
8464     });
8465     Roo.grid.ColumnModel.superclass.constructor.call(this);
8466 };
8467 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8468     /**
8469      * @cfg {String} header The header text to display in the Grid view.
8470      */
8471         /**
8472      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8473      */
8474         /**
8475      * @cfg {String} smHeader Header at Bootsrap Small width
8476      */
8477         /**
8478      * @cfg {String} mdHeader Header at Bootsrap Medium width
8479      */
8480         /**
8481      * @cfg {String} lgHeader Header at Bootsrap Large width
8482      */
8483         /**
8484      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8485      */
8486     /**
8487      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8488      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8489      * specified, the column's index is used as an index into the Record's data Array.
8490      */
8491     /**
8492      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8493      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8494      */
8495     /**
8496      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8497      * Defaults to the value of the {@link #defaultSortable} property.
8498      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8499      */
8500     /**
8501      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8505      */
8506     /**
8507      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8508      */
8509     /**
8510      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8511      */
8512     /**
8513      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8514      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8515      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8516      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8517      */
8518        /**
8519      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8520      */
8521     /**
8522      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8523      */
8524     /**
8525      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8526      */
8527     /**
8528      * @cfg {String} cursor (Optional)
8529      */
8530     /**
8531      * @cfg {String} tooltip (Optional)
8532      */
8533     /**
8534      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8541      */
8542     /**
8543      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8544      */
8545         /**
8546      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8547      */
8548     /**
8549      * Returns the id of the column at the specified index.
8550      * @param {Number} index The column index
8551      * @return {String} the id
8552      */
8553     getColumnId : function(index){
8554         return this.config[index].id;
8555     },
8556
8557     /**
8558      * Returns the column for a specified id.
8559      * @param {String} id The column id
8560      * @return {Object} the column
8561      */
8562     getColumnById : function(id){
8563         return this.lookup[id];
8564     },
8565
8566     
8567     /**
8568      * Returns the column Object for a specified dataIndex.
8569      * @param {String} dataIndex The column dataIndex
8570      * @return {Object|Boolean} the column or false if not found
8571      */
8572     getColumnByDataIndex: function(dataIndex){
8573         var index = this.findColumnIndex(dataIndex);
8574         return index > -1 ? this.config[index] : false;
8575     },
8576     
8577     /**
8578      * Returns the index for a specified column id.
8579      * @param {String} id The column id
8580      * @return {Number} the index, or -1 if not found
8581      */
8582     getIndexById : function(id){
8583         for(var i = 0, len = this.config.length; i < len; i++){
8584             if(this.config[i].id == id){
8585                 return i;
8586             }
8587         }
8588         return -1;
8589     },
8590     
8591     /**
8592      * Returns the index for a specified column dataIndex.
8593      * @param {String} dataIndex The column dataIndex
8594      * @return {Number} the index, or -1 if not found
8595      */
8596     
8597     findColumnIndex : function(dataIndex){
8598         for(var i = 0, len = this.config.length; i < len; i++){
8599             if(this.config[i].dataIndex == dataIndex){
8600                 return i;
8601             }
8602         }
8603         return -1;
8604     },
8605     
8606     
8607     moveColumn : function(oldIndex, newIndex){
8608         var c = this.config[oldIndex];
8609         this.config.splice(oldIndex, 1);
8610         this.config.splice(newIndex, 0, c);
8611         this.dataMap = null;
8612         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8613     },
8614
8615     isLocked : function(colIndex){
8616         return this.config[colIndex].locked === true;
8617     },
8618
8619     setLocked : function(colIndex, value, suppressEvent){
8620         if(this.isLocked(colIndex) == value){
8621             return;
8622         }
8623         this.config[colIndex].locked = value;
8624         if(!suppressEvent){
8625             this.fireEvent("columnlockchange", this, colIndex, value);
8626         }
8627     },
8628
8629     getTotalLockedWidth : function(){
8630         var totalWidth = 0;
8631         for(var i = 0; i < this.config.length; i++){
8632             if(this.isLocked(i) && !this.isHidden(i)){
8633                 this.totalWidth += this.getColumnWidth(i);
8634             }
8635         }
8636         return totalWidth;
8637     },
8638
8639     getLockedCount : function(){
8640         for(var i = 0, len = this.config.length; i < len; i++){
8641             if(!this.isLocked(i)){
8642                 return i;
8643             }
8644         }
8645         
8646         return this.config.length;
8647     },
8648
8649     /**
8650      * Returns the number of columns.
8651      * @return {Number}
8652      */
8653     getColumnCount : function(visibleOnly){
8654         if(visibleOnly === true){
8655             var c = 0;
8656             for(var i = 0, len = this.config.length; i < len; i++){
8657                 if(!this.isHidden(i)){
8658                     c++;
8659                 }
8660             }
8661             return c;
8662         }
8663         return this.config.length;
8664     },
8665
8666     /**
8667      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8668      * @param {Function} fn
8669      * @param {Object} scope (optional)
8670      * @return {Array} result
8671      */
8672     getColumnsBy : function(fn, scope){
8673         var r = [];
8674         for(var i = 0, len = this.config.length; i < len; i++){
8675             var c = this.config[i];
8676             if(fn.call(scope||this, c, i) === true){
8677                 r[r.length] = c;
8678             }
8679         }
8680         return r;
8681     },
8682
8683     /**
8684      * Returns true if the specified column is sortable.
8685      * @param {Number} col The column index
8686      * @return {Boolean}
8687      */
8688     isSortable : function(col){
8689         if(typeof this.config[col].sortable == "undefined"){
8690             return this.defaultSortable;
8691         }
8692         return this.config[col].sortable;
8693     },
8694
8695     /**
8696      * Returns the rendering (formatting) function defined for the column.
8697      * @param {Number} col The column index.
8698      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8699      */
8700     getRenderer : function(col){
8701         if(!this.config[col].renderer){
8702             return Roo.grid.ColumnModel.defaultRenderer;
8703         }
8704         return this.config[col].renderer;
8705     },
8706
8707     /**
8708      * Sets the rendering (formatting) function for a column.
8709      * @param {Number} col The column index
8710      * @param {Function} fn The function to use to process the cell's raw data
8711      * to return HTML markup for the grid view. The render function is called with
8712      * the following parameters:<ul>
8713      * <li>Data value.</li>
8714      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8715      * <li>css A CSS style string to apply to the table cell.</li>
8716      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8717      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8718      * <li>Row index</li>
8719      * <li>Column index</li>
8720      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8721      */
8722     setRenderer : function(col, fn){
8723         this.config[col].renderer = fn;
8724     },
8725
8726     /**
8727      * Returns the width for the specified column.
8728      * @param {Number} col The column index
8729      * @param (optional) {String} gridSize bootstrap width size.
8730      * @return {Number}
8731      */
8732     getColumnWidth : function(col, gridSize)
8733         {
8734                 var cfg = this.config[col];
8735                 
8736                 if (typeof(gridSize) == 'undefined') {
8737                         return cfg.width * 1 || this.defaultWidth;
8738                 }
8739                 if (gridSize === false) { // if we set it..
8740                         return cfg.width || false;
8741                 }
8742                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8743                 
8744                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8745                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8746                                 continue;
8747                         }
8748                         return cfg[ sizes[i] ];
8749                 }
8750                 return 1;
8751                 
8752     },
8753
8754     /**
8755      * Sets the width for a column.
8756      * @param {Number} col The column index
8757      * @param {Number} width The new width
8758      */
8759     setColumnWidth : function(col, width, suppressEvent){
8760         this.config[col].width = width;
8761         this.totalWidth = null;
8762         if(!suppressEvent){
8763              this.fireEvent("widthchange", this, col, width);
8764         }
8765     },
8766
8767     /**
8768      * Returns the total width of all columns.
8769      * @param {Boolean} includeHidden True to include hidden column widths
8770      * @return {Number}
8771      */
8772     getTotalWidth : function(includeHidden){
8773         if(!this.totalWidth){
8774             this.totalWidth = 0;
8775             for(var i = 0, len = this.config.length; i < len; i++){
8776                 if(includeHidden || !this.isHidden(i)){
8777                     this.totalWidth += this.getColumnWidth(i);
8778                 }
8779             }
8780         }
8781         return this.totalWidth;
8782     },
8783
8784     /**
8785      * Returns the header for the specified column.
8786      * @param {Number} col The column index
8787      * @return {String}
8788      */
8789     getColumnHeader : function(col){
8790         return this.config[col].header;
8791     },
8792
8793     /**
8794      * Sets the header for a column.
8795      * @param {Number} col The column index
8796      * @param {String} header The new header
8797      */
8798     setColumnHeader : function(col, header){
8799         this.config[col].header = header;
8800         this.fireEvent("headerchange", this, col, header);
8801     },
8802
8803     /**
8804      * Returns the tooltip for the specified column.
8805      * @param {Number} col The column index
8806      * @return {String}
8807      */
8808     getColumnTooltip : function(col){
8809             return this.config[col].tooltip;
8810     },
8811     /**
8812      * Sets the tooltip for a column.
8813      * @param {Number} col The column index
8814      * @param {String} tooltip The new tooltip
8815      */
8816     setColumnTooltip : function(col, tooltip){
8817             this.config[col].tooltip = tooltip;
8818     },
8819
8820     /**
8821      * Returns the dataIndex for the specified column.
8822      * @param {Number} col The column index
8823      * @return {Number}
8824      */
8825     getDataIndex : function(col){
8826         return this.config[col].dataIndex;
8827     },
8828
8829     /**
8830      * Sets the dataIndex for a column.
8831      * @param {Number} col The column index
8832      * @param {Number} dataIndex The new dataIndex
8833      */
8834     setDataIndex : function(col, dataIndex){
8835         this.config[col].dataIndex = dataIndex;
8836     },
8837
8838     
8839     
8840     /**
8841      * Returns true if the cell is editable.
8842      * @param {Number} colIndex The column index
8843      * @param {Number} rowIndex The row index - this is nto actually used..?
8844      * @return {Boolean}
8845      */
8846     isCellEditable : function(colIndex, rowIndex){
8847         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8848     },
8849
8850     /**
8851      * Returns the editor defined for the cell/column.
8852      * return false or null to disable editing.
8853      * @param {Number} colIndex The column index
8854      * @param {Number} rowIndex The row index
8855      * @return {Object}
8856      */
8857     getCellEditor : function(colIndex, rowIndex){
8858         return this.config[colIndex].editor;
8859     },
8860
8861     /**
8862      * Sets if a column is editable.
8863      * @param {Number} col The column index
8864      * @param {Boolean} editable True if the column is editable
8865      */
8866     setEditable : function(col, editable){
8867         this.config[col].editable = editable;
8868     },
8869
8870
8871     /**
8872      * Returns true if the column is hidden.
8873      * @param {Number} colIndex The column index
8874      * @return {Boolean}
8875      */
8876     isHidden : function(colIndex){
8877         return this.config[colIndex].hidden;
8878     },
8879
8880
8881     /**
8882      * Returns true if the column width cannot be changed
8883      */
8884     isFixed : function(colIndex){
8885         return this.config[colIndex].fixed;
8886     },
8887
8888     /**
8889      * Returns true if the column can be resized
8890      * @return {Boolean}
8891      */
8892     isResizable : function(colIndex){
8893         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8894     },
8895     /**
8896      * Sets if a column is hidden.
8897      * @param {Number} colIndex The column index
8898      * @param {Boolean} hidden True if the column is hidden
8899      */
8900     setHidden : function(colIndex, hidden){
8901         this.config[colIndex].hidden = hidden;
8902         this.totalWidth = null;
8903         this.fireEvent("hiddenchange", this, colIndex, hidden);
8904     },
8905
8906     /**
8907      * Sets the editor for a column.
8908      * @param {Number} col The column index
8909      * @param {Object} editor The editor object
8910      */
8911     setEditor : function(col, editor){
8912         this.config[col].editor = editor;
8913     },
8914     /**
8915      * Add a column (experimental...) - defaults to adding to the end..
8916      * @param {Object} config 
8917     */
8918     addColumn : function(c)
8919     {
8920     
8921         var i = this.config.length;
8922         this.config[i] = c;
8923         
8924         if(typeof c.dataIndex == "undefined"){
8925             c.dataIndex = i;
8926         }
8927         if(typeof c.renderer == "string"){
8928             c.renderer = Roo.util.Format[c.renderer];
8929         }
8930         if(typeof c.id == "undefined"){
8931             c.id = Roo.id();
8932         }
8933         if(c.editor && c.editor.xtype){
8934             c.editor  = Roo.factory(c.editor, Roo.grid);
8935         }
8936         if(c.editor && c.editor.isFormField){
8937             c.editor = new Roo.grid.GridEditor(c.editor);
8938         }
8939         this.lookup[c.id] = c;
8940     }
8941     
8942 });
8943
8944 Roo.grid.ColumnModel.defaultRenderer = function(value)
8945 {
8946     if(typeof value == "object") {
8947         return value;
8948     }
8949         if(typeof value == "string" && value.length < 1){
8950             return "&#160;";
8951         }
8952     
8953         return String.format("{0}", value);
8954 };
8955
8956 // Alias for backwards compatibility
8957 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8958 /*
8959  * Based on:
8960  * Ext JS Library 1.1.1
8961  * Copyright(c) 2006-2007, Ext JS, LLC.
8962  *
8963  * Originally Released Under LGPL - original licence link has changed is not relivant.
8964  *
8965  * Fork - LGPL
8966  * <script type="text/javascript">
8967  */
8968  
8969 /**
8970  * @class Roo.LoadMask
8971  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8972  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8973  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8974  * element's UpdateManager load indicator and will be destroyed after the initial load.
8975  * @constructor
8976  * Create a new LoadMask
8977  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8978  * @param {Object} config The config object
8979  */
8980 Roo.LoadMask = function(el, config){
8981     this.el = Roo.get(el);
8982     Roo.apply(this, config);
8983     if(this.store){
8984         this.store.on('beforeload', this.onBeforeLoad, this);
8985         this.store.on('load', this.onLoad, this);
8986         this.store.on('loadexception', this.onLoadException, this);
8987         this.removeMask = false;
8988     }else{
8989         var um = this.el.getUpdateManager();
8990         um.showLoadIndicator = false; // disable the default indicator
8991         um.on('beforeupdate', this.onBeforeLoad, this);
8992         um.on('update', this.onLoad, this);
8993         um.on('failure', this.onLoad, this);
8994         this.removeMask = true;
8995     }
8996 };
8997
8998 Roo.LoadMask.prototype = {
8999     /**
9000      * @cfg {Boolean} removeMask
9001      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9002      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9003      */
9004     removeMask : false,
9005     /**
9006      * @cfg {String} msg
9007      * The text to display in a centered loading message box (defaults to 'Loading...')
9008      */
9009     msg : 'Loading...',
9010     /**
9011      * @cfg {String} msgCls
9012      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9013      */
9014     msgCls : 'x-mask-loading',
9015
9016     /**
9017      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9018      * @type Boolean
9019      */
9020     disabled: false,
9021
9022     /**
9023      * Disables the mask to prevent it from being displayed
9024      */
9025     disable : function(){
9026        this.disabled = true;
9027     },
9028
9029     /**
9030      * Enables the mask so that it can be displayed
9031      */
9032     enable : function(){
9033         this.disabled = false;
9034     },
9035     
9036     onLoadException : function()
9037     {
9038         Roo.log(arguments);
9039         
9040         if (typeof(arguments[3]) != 'undefined') {
9041             Roo.MessageBox.alert("Error loading",arguments[3]);
9042         } 
9043         /*
9044         try {
9045             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9046                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9047             }   
9048         } catch(e) {
9049             
9050         }
9051         */
9052     
9053         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9054     },
9055     // private
9056     onLoad : function()
9057     {
9058         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9059     },
9060
9061     // private
9062     onBeforeLoad : function(){
9063         if(!this.disabled){
9064             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9065         }
9066     },
9067
9068     // private
9069     destroy : function(){
9070         if(this.store){
9071             this.store.un('beforeload', this.onBeforeLoad, this);
9072             this.store.un('load', this.onLoad, this);
9073             this.store.un('loadexception', this.onLoadException, this);
9074         }else{
9075             var um = this.el.getUpdateManager();
9076             um.un('beforeupdate', this.onBeforeLoad, this);
9077             um.un('update', this.onLoad, this);
9078             um.un('failure', this.onLoad, this);
9079         }
9080     }
9081 };/**
9082  * @class Roo.bootstrap.Table
9083  * @licence LGBL
9084  * @extends Roo.bootstrap.Component
9085  * @children Roo.bootstrap.TableBody
9086  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9087  * Similar to Roo.grid.Grid
9088  * <pre><code>
9089  var table = Roo.factory({
9090     xtype : 'Table',
9091     xns : Roo.bootstrap,
9092     autoSizeColumns: true,
9093     
9094     
9095     store : {
9096         xtype : 'Store',
9097         xns : Roo.data,
9098         remoteSort : true,
9099         sortInfo : { direction : 'ASC', field: 'name' },
9100         proxy : {
9101            xtype : 'HttpProxy',
9102            xns : Roo.data,
9103            method : 'GET',
9104            url : 'https://example.com/some.data.url.json'
9105         },
9106         reader : {
9107            xtype : 'JsonReader',
9108            xns : Roo.data,
9109            fields : [ 'id', 'name', whatever' ],
9110            id : 'id',
9111            root : 'data'
9112         }
9113     },
9114     cm : [
9115         {
9116             xtype : 'ColumnModel',
9117             xns : Roo.grid,
9118             align : 'center',
9119             cursor : 'pointer',
9120             dataIndex : 'is_in_group',
9121             header : "Name",
9122             sortable : true,
9123             renderer : function(v, x , r) {  
9124             
9125                 return String.format("{0}", v)
9126             }
9127             width : 3
9128         } // more columns..
9129     ],
9130     selModel : {
9131         xtype : 'RowSelectionModel',
9132         xns : Roo.bootstrap.Table
9133         // you can add listeners to catch selection change here....
9134     }
9135      
9136
9137  });
9138  // set any options
9139  grid.render(Roo.get("some-div"));
9140 </code></pre>
9141
9142 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9143
9144
9145
9146  *
9147  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9148  * @cfg {Roo.data.Store} store The data store to use
9149  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9150  * 
9151  * @cfg {String} cls table class
9152  *
9153  * 
9154  * @cfg {boolean} striped Should the rows be alternative striped
9155  * @cfg {boolean} bordered Add borders to the table
9156  * @cfg {boolean} hover Add hover highlighting
9157  * @cfg {boolean} condensed Format condensed
9158  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9159  *                also adds table-responsive (see bootstrap docs for details)
9160  * @cfg {Boolean} loadMask (true|false) default false
9161  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9162  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9163  * @cfg {Boolean} rowSelection (true|false) default false
9164  * @cfg {Boolean} cellSelection (true|false) default false
9165  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9166  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9167  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9168  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9169  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9170  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9171  * 
9172  * @constructor
9173  * Create a new Table
9174  * @param {Object} config The config object
9175  */
9176
9177 Roo.bootstrap.Table = function(config)
9178 {
9179     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9180      
9181     // BC...
9182     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9183     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9184     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9185     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9186     
9187     this.view = this; // compat with grid.
9188     
9189     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9190     if (this.sm) {
9191         this.sm.grid = this;
9192         this.selModel = Roo.factory(this.sm, Roo.grid);
9193         this.sm = this.selModel;
9194         this.sm.xmodule = this.xmodule || false;
9195     }
9196     
9197     if (this.cm && typeof(this.cm.config) == 'undefined') {
9198         this.colModel = new Roo.grid.ColumnModel(this.cm);
9199         this.cm = this.colModel;
9200         this.cm.xmodule = this.xmodule || false;
9201     }
9202     if (this.store) {
9203         this.store= Roo.factory(this.store, Roo.data);
9204         this.ds = this.store;
9205         this.ds.xmodule = this.xmodule || false;
9206          
9207     }
9208     if (this.footer && this.store) {
9209         this.footer.dataSource = this.ds;
9210         this.footer = Roo.factory(this.footer);
9211     }
9212     
9213     /** @private */
9214     this.addEvents({
9215         /**
9216          * @event cellclick
9217          * Fires when a cell is clicked
9218          * @param {Roo.bootstrap.Table} this
9219          * @param {Roo.Element} el
9220          * @param {Number} rowIndex
9221          * @param {Number} columnIndex
9222          * @param {Roo.EventObject} e
9223          */
9224         "cellclick" : true,
9225         /**
9226          * @event celldblclick
9227          * Fires when a cell is double clicked
9228          * @param {Roo.bootstrap.Table} this
9229          * @param {Roo.Element} el
9230          * @param {Number} rowIndex
9231          * @param {Number} columnIndex
9232          * @param {Roo.EventObject} e
9233          */
9234         "celldblclick" : true,
9235         /**
9236          * @event rowclick
9237          * Fires when a row is clicked
9238          * @param {Roo.bootstrap.Table} this
9239          * @param {Roo.Element} el
9240          * @param {Number} rowIndex
9241          * @param {Roo.EventObject} e
9242          */
9243         "rowclick" : true,
9244         /**
9245          * @event rowdblclick
9246          * Fires when a row is double clicked
9247          * @param {Roo.bootstrap.Table} this
9248          * @param {Roo.Element} el
9249          * @param {Number} rowIndex
9250          * @param {Roo.EventObject} e
9251          */
9252         "rowdblclick" : true,
9253         /**
9254          * @event mouseover
9255          * Fires when a mouseover occur
9256          * @param {Roo.bootstrap.Table} this
9257          * @param {Roo.Element} el
9258          * @param {Number} rowIndex
9259          * @param {Number} columnIndex
9260          * @param {Roo.EventObject} e
9261          */
9262         "mouseover" : true,
9263         /**
9264          * @event mouseout
9265          * Fires when a mouseout occur
9266          * @param {Roo.bootstrap.Table} this
9267          * @param {Roo.Element} el
9268          * @param {Number} rowIndex
9269          * @param {Number} columnIndex
9270          * @param {Roo.EventObject} e
9271          */
9272         "mouseout" : true,
9273         /**
9274          * @event rowclass
9275          * Fires when a row is rendered, so you can change add a style to it.
9276          * @param {Roo.bootstrap.Table} this
9277          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9278          */
9279         'rowclass' : true,
9280           /**
9281          * @event rowsrendered
9282          * Fires when all the  rows have been rendered
9283          * @param {Roo.bootstrap.Table} this
9284          */
9285         'rowsrendered' : true,
9286         /**
9287          * @event contextmenu
9288          * The raw contextmenu event for the entire grid.
9289          * @param {Roo.EventObject} e
9290          */
9291         "contextmenu" : true,
9292         /**
9293          * @event rowcontextmenu
9294          * Fires when a row is right clicked
9295          * @param {Roo.bootstrap.Table} this
9296          * @param {Number} rowIndex
9297          * @param {Roo.EventObject} e
9298          */
9299         "rowcontextmenu" : true,
9300         /**
9301          * @event cellcontextmenu
9302          * Fires when a cell is right clicked
9303          * @param {Roo.bootstrap.Table} this
9304          * @param {Number} rowIndex
9305          * @param {Number} cellIndex
9306          * @param {Roo.EventObject} e
9307          */
9308          "cellcontextmenu" : true,
9309          /**
9310          * @event headercontextmenu
9311          * Fires when a header is right clicked
9312          * @param {Roo.bootstrap.Table} this
9313          * @param {Number} columnIndex
9314          * @param {Roo.EventObject} e
9315          */
9316         "headercontextmenu" : true,
9317         /**
9318          * @event mousedown
9319          * The raw mousedown event for the entire grid.
9320          * @param {Roo.EventObject} e
9321          */
9322         "mousedown" : true
9323         
9324     });
9325 };
9326
9327 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9328     
9329     cls: false,
9330     
9331     striped : false,
9332     scrollBody : false,
9333     bordered: false,
9334     hover:  false,
9335     condensed : false,
9336     responsive : false,
9337     sm : false,
9338     cm : false,
9339     store : false,
9340     loadMask : false,
9341     footerShow : true,
9342     headerShow : true,
9343     enableColumnResize: true,
9344   
9345     rowSelection : false,
9346     cellSelection : false,
9347     layout : false,
9348
9349     minColumnWidth : 50,
9350     
9351     // Roo.Element - the tbody
9352     bodyEl: false,  // <tbody> Roo.Element - thead element    
9353     headEl: false,  // <thead> Roo.Element - thead element
9354     resizeProxy : false, // proxy element for dragging?
9355
9356
9357     
9358     container: false, // used by gridpanel...
9359     
9360     lazyLoad : false,
9361     
9362     CSS : Roo.util.CSS,
9363     
9364     auto_hide_footer : false,
9365     
9366     view: false, // actually points to this..
9367     
9368     getAutoCreate : function()
9369     {
9370         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9371         
9372         cfg = {
9373             tag: 'table',
9374             cls : 'table', 
9375             cn : []
9376         };
9377         // this get's auto added by panel.Grid
9378         if (this.scrollBody) {
9379             cfg.cls += ' table-body-fixed';
9380         }    
9381         if (this.striped) {
9382             cfg.cls += ' table-striped';
9383         }
9384         
9385         if (this.hover) {
9386             cfg.cls += ' table-hover';
9387         }
9388         if (this.bordered) {
9389             cfg.cls += ' table-bordered';
9390         }
9391         if (this.condensed) {
9392             cfg.cls += ' table-condensed';
9393         }
9394         
9395         if (this.responsive) {
9396             cfg.cls += ' table-responsive';
9397         }
9398         
9399         if (this.cls) {
9400             cfg.cls+=  ' ' +this.cls;
9401         }
9402         
9403         
9404         
9405         if (this.layout) {
9406             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9407         }
9408         
9409         if(this.store || this.cm){
9410             if(this.headerShow){
9411                 cfg.cn.push(this.renderHeader());
9412             }
9413             
9414             cfg.cn.push(this.renderBody());
9415             
9416             if(this.footerShow){
9417                 cfg.cn.push(this.renderFooter());
9418             }
9419             // where does this come from?
9420             //cfg.cls+=  ' TableGrid';
9421         }
9422         
9423         return { cn : [ cfg ] };
9424     },
9425     
9426     initEvents : function()
9427     {   
9428         if(!this.store || !this.cm){
9429             return;
9430         }
9431         if (this.selModel) {
9432             this.selModel.initEvents();
9433         }
9434         
9435         
9436         //Roo.log('initEvents with ds!!!!');
9437         
9438         this.bodyEl = this.el.select('tbody', true).first();
9439         this.headEl = this.el.select('thead', true).first();
9440         this.mainFoot = this.el.select('tfoot', true).first();
9441         
9442         
9443         
9444         
9445         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9446             e.on('click', this.sort, this);
9447         }, this);
9448         
9449         
9450         // why is this done????? = it breaks dialogs??
9451         //this.parent().el.setStyle('position', 'relative');
9452         
9453         
9454         if (this.footer) {
9455             this.footer.parentId = this.id;
9456             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9457             
9458             if(this.lazyLoad){
9459                 this.el.select('tfoot tr td').first().addClass('hide');
9460             }
9461         } 
9462         
9463         if(this.loadMask) {
9464             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9465         }
9466         
9467         this.store.on('load', this.onLoad, this);
9468         this.store.on('beforeload', this.onBeforeLoad, this);
9469         this.store.on('update', this.onUpdate, this);
9470         this.store.on('add', this.onAdd, this);
9471         this.store.on("clear", this.clear, this);
9472         
9473         this.el.on("contextmenu", this.onContextMenu, this);
9474         
9475         
9476         this.cm.on("headerchange", this.onHeaderChange, this);
9477         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9478
9479  //?? does bodyEl get replaced on render?
9480         this.bodyEl.on("click", this.onClick, this);
9481         this.bodyEl.on("dblclick", this.onDblClick, this);        
9482         this.bodyEl.on('scroll', this.onBodyScroll, this);
9483
9484         // guessing mainbody will work - this relays usually caught by selmodel at present.
9485         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9486   
9487   
9488         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9489         
9490   
9491         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9492             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9493         }
9494         
9495         this.initCSS();
9496     },
9497     // Compatibility with grid - we implement all the view features at present.
9498     getView : function()
9499     {
9500         return this;
9501     },
9502     
9503     initCSS : function()
9504     {
9505         
9506         
9507         var cm = this.cm, styles = [];
9508         this.CSS.removeStyleSheet(this.id + '-cssrules');
9509         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9510         // we can honour xs/sm/md/xl  as widths...
9511         // we first have to decide what widht we are currently at...
9512         var sz = Roo.getGridSize();
9513         
9514         var total = 0;
9515         var last = -1;
9516         var cols = []; // visable cols.
9517         var total_abs = 0;
9518         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9519             var w = cm.getColumnWidth(i, false);
9520             if(cm.isHidden(i)){
9521                 cols.push( { rel : false, abs : 0 });
9522                 continue;
9523             }
9524             if (w !== false) {
9525                 cols.push( { rel : false, abs : w });
9526                 total_abs += w;
9527                 last = i; // not really..
9528                 continue;
9529             }
9530             var w = cm.getColumnWidth(i, sz);
9531             if (w > 0) {
9532                 last = i
9533             }
9534             total += w;
9535             cols.push( { rel : w, abs : false });
9536         }
9537         
9538         var avail = this.bodyEl.dom.clientWidth - total_abs;
9539         
9540         var unitWidth = Math.floor(avail / total);
9541         var rem = avail - (unitWidth * total);
9542         
9543         var hidden, width, pos = 0 , splithide , left;
9544         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9545             
9546             hidden = 'display:none;';
9547             left = '';
9548             width  = 'width:0px;';
9549             splithide = '';
9550             if(!cm.isHidden(i)){
9551                 hidden = '';
9552                 
9553                 
9554                 // we can honour xs/sm/md/xl ?
9555                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9556                 if (w===0) {
9557                     hidden = 'display:none;';
9558                 }
9559                 // width should return a small number...
9560                 if (i == last) {
9561                     w+=rem; // add the remaining with..
9562                 }
9563                 pos += w;
9564                 left = "left:" + (pos -4) + "px;";
9565                 width = "width:" + w+ "px;";
9566                 
9567             }
9568             if (this.responsive) {
9569                 width = '';
9570                 left = '';
9571                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9572                 splithide = 'display: none;';
9573             }
9574             
9575             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9576             if (this.headEl) {
9577                 if (i == last) {
9578                     splithide = 'display:none;';
9579                 }
9580                 
9581                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9582                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9583                 );
9584             }
9585             
9586         }
9587         //Roo.log(styles.join(''));
9588         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9589         
9590     },
9591     
9592     
9593     
9594     onContextMenu : function(e, t)
9595     {
9596         this.processEvent("contextmenu", e);
9597     },
9598     
9599     processEvent : function(name, e)
9600     {
9601         if (name != 'touchstart' ) {
9602             this.fireEvent(name, e);    
9603         }
9604         
9605         var t = e.getTarget();
9606         
9607         var cell = Roo.get(t);
9608         
9609         if(!cell){
9610             return;
9611         }
9612         
9613         if(cell.findParent('tfoot', false, true)){
9614             return;
9615         }
9616         
9617         if(cell.findParent('thead', false, true)){
9618             
9619             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9620                 cell = Roo.get(t).findParent('th', false, true);
9621                 if (!cell) {
9622                     Roo.log("failed to find th in thead?");
9623                     Roo.log(e.getTarget());
9624                     return;
9625                 }
9626             }
9627             
9628             var cellIndex = cell.dom.cellIndex;
9629             
9630             var ename = name == 'touchstart' ? 'click' : name;
9631             this.fireEvent("header" + ename, this, cellIndex, e);
9632             
9633             return;
9634         }
9635         
9636         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9637             cell = Roo.get(t).findParent('td', false, true);
9638             if (!cell) {
9639                 Roo.log("failed to find th in tbody?");
9640                 Roo.log(e.getTarget());
9641                 return;
9642             }
9643         }
9644         
9645         var row = cell.findParent('tr', false, true);
9646         var cellIndex = cell.dom.cellIndex;
9647         var rowIndex = row.dom.rowIndex - 1;
9648         
9649         if(row !== false){
9650             
9651             this.fireEvent("row" + name, this, rowIndex, e);
9652             
9653             if(cell !== false){
9654             
9655                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9656             }
9657         }
9658         
9659     },
9660     
9661     onMouseover : function(e, el)
9662     {
9663         var cell = Roo.get(el);
9664         
9665         if(!cell){
9666             return;
9667         }
9668         
9669         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9670             cell = cell.findParent('td', false, true);
9671         }
9672         
9673         var row = cell.findParent('tr', false, true);
9674         var cellIndex = cell.dom.cellIndex;
9675         var rowIndex = row.dom.rowIndex - 1; // start from 0
9676         
9677         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9678         
9679     },
9680     
9681     onMouseout : function(e, el)
9682     {
9683         var cell = Roo.get(el);
9684         
9685         if(!cell){
9686             return;
9687         }
9688         
9689         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9690             cell = cell.findParent('td', false, true);
9691         }
9692         
9693         var row = cell.findParent('tr', false, true);
9694         var cellIndex = cell.dom.cellIndex;
9695         var rowIndex = row.dom.rowIndex - 1; // start from 0
9696         
9697         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9698         
9699     },
9700     
9701     onClick : function(e, el)
9702     {
9703         var cell = Roo.get(el);
9704         
9705         if(!cell || (!this.cellSelection && !this.rowSelection)){
9706             return;
9707         }
9708         
9709         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9710             cell = cell.findParent('td', false, true);
9711         }
9712         
9713         if(!cell || typeof(cell) == 'undefined'){
9714             return;
9715         }
9716         
9717         var row = cell.findParent('tr', false, true);
9718         
9719         if(!row || typeof(row) == 'undefined'){
9720             return;
9721         }
9722         
9723         var cellIndex = cell.dom.cellIndex;
9724         var rowIndex = this.getRowIndex(row);
9725         
9726         // why??? - should these not be based on SelectionModel?
9727         //if(this.cellSelection){
9728             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9729         //}
9730         
9731         //if(this.rowSelection){
9732             this.fireEvent('rowclick', this, row, rowIndex, e);
9733         //}
9734          
9735     },
9736         
9737     onDblClick : function(e,el)
9738     {
9739         var cell = Roo.get(el);
9740         
9741         if(!cell || (!this.cellSelection && !this.rowSelection)){
9742             return;
9743         }
9744         
9745         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9746             cell = cell.findParent('td', false, true);
9747         }
9748         
9749         if(!cell || typeof(cell) == 'undefined'){
9750             return;
9751         }
9752         
9753         var row = cell.findParent('tr', false, true);
9754         
9755         if(!row || typeof(row) == 'undefined'){
9756             return;
9757         }
9758         
9759         var cellIndex = cell.dom.cellIndex;
9760         var rowIndex = this.getRowIndex(row);
9761         
9762         if(this.cellSelection){
9763             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9764         }
9765         
9766         if(this.rowSelection){
9767             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9768         }
9769     },
9770     findRowIndex : function(el)
9771     {
9772         var cell = Roo.get(el);
9773         if(!cell) {
9774             return false;
9775         }
9776         var row = cell.findParent('tr', false, true);
9777         
9778         if(!row || typeof(row) == 'undefined'){
9779             return false;
9780         }
9781         return this.getRowIndex(row);
9782     },
9783     sort : function(e,el)
9784     {
9785         var col = Roo.get(el);
9786         
9787         if(!col.hasClass('sortable')){
9788             return;
9789         }
9790         
9791         var sort = col.attr('sort');
9792         var dir = 'ASC';
9793         
9794         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9795             dir = 'DESC';
9796         }
9797         
9798         this.store.sortInfo = {field : sort, direction : dir};
9799         
9800         if (this.footer) {
9801             Roo.log("calling footer first");
9802             this.footer.onClick('first');
9803         } else {
9804         
9805             this.store.load({ params : { start : 0 } });
9806         }
9807     },
9808     
9809     renderHeader : function()
9810     {
9811         var header = {
9812             tag: 'thead',
9813             cn : []
9814         };
9815         
9816         var cm = this.cm;
9817         this.totalWidth = 0;
9818         
9819         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9820             
9821             var config = cm.config[i];
9822             
9823             var c = {
9824                 tag: 'th',
9825                 cls : 'x-hcol-' + i,
9826                 style : '',
9827                 
9828                 html: cm.getColumnHeader(i)
9829             };
9830             
9831             var tooltip = cm.getColumnTooltip(i);
9832             if (tooltip) {
9833                 c.tooltip = tooltip;
9834             }
9835             
9836             
9837             var hh = '';
9838             
9839             if(typeof(config.sortable) != 'undefined' && config.sortable){
9840                 c.cls += ' sortable';
9841                 c.html = '<i class="fa"></i>' + c.html;
9842             }
9843             
9844             // could use BS4 hidden-..-down 
9845             
9846             if(typeof(config.lgHeader) != 'undefined'){
9847                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9848             }
9849             
9850             if(typeof(config.mdHeader) != 'undefined'){
9851                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9852             }
9853             
9854             if(typeof(config.smHeader) != 'undefined'){
9855                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9856             }
9857             
9858             if(typeof(config.xsHeader) != 'undefined'){
9859                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9860             }
9861             
9862             if(hh.length){
9863                 c.html = hh;
9864             }
9865             
9866             if(typeof(config.tooltip) != 'undefined'){
9867                 c.tooltip = config.tooltip;
9868             }
9869             
9870             if(typeof(config.colspan) != 'undefined'){
9871                 c.colspan = config.colspan;
9872             }
9873             
9874             // hidden is handled by CSS now
9875             
9876             if(typeof(config.dataIndex) != 'undefined'){
9877                 c.sort = config.dataIndex;
9878             }
9879             
9880            
9881             
9882             if(typeof(config.align) != 'undefined' && config.align.length){
9883                 c.style += ' text-align:' + config.align + ';';
9884             }
9885             
9886             /* width is done in CSS
9887              *if(typeof(config.width) != 'undefined'){
9888                 c.style += ' width:' + config.width + 'px;';
9889                 this.totalWidth += config.width;
9890             } else {
9891                 this.totalWidth += 100; // assume minimum of 100 per column?
9892             }
9893             */
9894             
9895             if(typeof(config.cls) != 'undefined'){
9896                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9897             }
9898             // this is the bit that doesnt reall work at all...
9899             
9900             if (this.responsive) {
9901                  
9902             
9903                 ['xs','sm','md','lg'].map(function(size){
9904                     
9905                     if(typeof(config[size]) == 'undefined'){
9906                         return;
9907                     }
9908                      
9909                     if (!config[size]) { // 0 = hidden
9910                         // BS 4 '0' is treated as hide that column and below.
9911                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9912                         return;
9913                     }
9914                     
9915                     c.cls += ' col-' + size + '-' + config[size] + (
9916                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9917                     );
9918                     
9919                     
9920                 });
9921             }
9922             // at the end?
9923             
9924             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9925             
9926             
9927             
9928             
9929             header.cn.push(c)
9930         }
9931         
9932         return header;
9933     },
9934     
9935     renderBody : function()
9936     {
9937         var body = {
9938             tag: 'tbody',
9939             cn : [
9940                 {
9941                     tag: 'tr',
9942                     cn : [
9943                         {
9944                             tag : 'td',
9945                             colspan :  this.cm.getColumnCount()
9946                         }
9947                     ]
9948                 }
9949             ]
9950         };
9951         
9952         return body;
9953     },
9954     
9955     renderFooter : function()
9956     {
9957         var footer = {
9958             tag: 'tfoot',
9959             cn : [
9960                 {
9961                     tag: 'tr',
9962                     cn : [
9963                         {
9964                             tag : 'td',
9965                             colspan :  this.cm.getColumnCount()
9966                         }
9967                     ]
9968                 }
9969             ]
9970         };
9971         
9972         return footer;
9973     },
9974     
9975     
9976     
9977     onLoad : function()
9978     {
9979 //        Roo.log('ds onload');
9980         this.clear();
9981         
9982         var _this = this;
9983         var cm = this.cm;
9984         var ds = this.store;
9985         
9986         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9987             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9988             if (_this.store.sortInfo) {
9989                     
9990                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9991                     e.select('i', true).addClass(['fa-arrow-up']);
9992                 }
9993                 
9994                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9995                     e.select('i', true).addClass(['fa-arrow-down']);
9996                 }
9997             }
9998         });
9999         
10000         var tbody =  this.bodyEl;
10001               
10002         if(ds.getCount() > 0){
10003             ds.data.each(function(d,rowIndex){
10004                 var row =  this.renderRow(cm, ds, rowIndex);
10005                 
10006                 tbody.createChild(row);
10007                 
10008                 var _this = this;
10009                 
10010                 if(row.cellObjects.length){
10011                     Roo.each(row.cellObjects, function(r){
10012                         _this.renderCellObject(r);
10013                     })
10014                 }
10015                 
10016             }, this);
10017         }
10018         
10019         var tfoot = this.el.select('tfoot', true).first();
10020         
10021         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10022             
10023             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10024             
10025             var total = this.ds.getTotalCount();
10026             
10027             if(this.footer.pageSize < total){
10028                 this.mainFoot.show();
10029             }
10030         }
10031         
10032         Roo.each(this.el.select('tbody td', true).elements, function(e){
10033             e.on('mouseover', _this.onMouseover, _this);
10034         });
10035         
10036         Roo.each(this.el.select('tbody td', true).elements, function(e){
10037             e.on('mouseout', _this.onMouseout, _this);
10038         });
10039         this.fireEvent('rowsrendered', this);
10040         
10041         this.autoSize();
10042         
10043         this.initCSS(); /// resize cols
10044
10045         
10046     },
10047     
10048     
10049     onUpdate : function(ds,record)
10050     {
10051         this.refreshRow(record);
10052         this.autoSize();
10053     },
10054     
10055     onRemove : function(ds, record, index, isUpdate){
10056         if(isUpdate !== true){
10057             this.fireEvent("beforerowremoved", this, index, record);
10058         }
10059         var bt = this.bodyEl.dom;
10060         
10061         var rows = this.el.select('tbody > tr', true).elements;
10062         
10063         if(typeof(rows[index]) != 'undefined'){
10064             bt.removeChild(rows[index].dom);
10065         }
10066         
10067 //        if(bt.rows[index]){
10068 //            bt.removeChild(bt.rows[index]);
10069 //        }
10070         
10071         if(isUpdate !== true){
10072             //this.stripeRows(index);
10073             //this.syncRowHeights(index, index);
10074             //this.layout();
10075             this.fireEvent("rowremoved", this, index, record);
10076         }
10077     },
10078     
10079     onAdd : function(ds, records, rowIndex)
10080     {
10081         //Roo.log('on Add called');
10082         // - note this does not handle multiple adding very well..
10083         var bt = this.bodyEl.dom;
10084         for (var i =0 ; i < records.length;i++) {
10085             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10086             //Roo.log(records[i]);
10087             //Roo.log(this.store.getAt(rowIndex+i));
10088             this.insertRow(this.store, rowIndex + i, false);
10089             return;
10090         }
10091         
10092     },
10093     
10094     
10095     refreshRow : function(record){
10096         var ds = this.store, index;
10097         if(typeof record == 'number'){
10098             index = record;
10099             record = ds.getAt(index);
10100         }else{
10101             index = ds.indexOf(record);
10102             if (index < 0) {
10103                 return; // should not happen - but seems to 
10104             }
10105         }
10106         this.insertRow(ds, index, true);
10107         this.autoSize();
10108         this.onRemove(ds, record, index+1, true);
10109         this.autoSize();
10110         //this.syncRowHeights(index, index);
10111         //this.layout();
10112         this.fireEvent("rowupdated", this, index, record);
10113     },
10114     // private - called by RowSelection
10115     onRowSelect : function(rowIndex){
10116         var row = this.getRowDom(rowIndex);
10117         row.addClass(['bg-info','info']);
10118     },
10119     // private - called by RowSelection
10120     onRowDeselect : function(rowIndex)
10121     {
10122         if (rowIndex < 0) {
10123             return;
10124         }
10125         var row = this.getRowDom(rowIndex);
10126         row.removeClass(['bg-info','info']);
10127     },
10128       /**
10129      * Focuses the specified row.
10130      * @param {Number} row The row index
10131      */
10132     focusRow : function(row)
10133     {
10134         //Roo.log('GridView.focusRow');
10135         var x = this.bodyEl.dom.scrollLeft;
10136         this.focusCell(row, 0, false);
10137         this.bodyEl.dom.scrollLeft = x;
10138
10139     },
10140      /**
10141      * Focuses the specified cell.
10142      * @param {Number} row The row index
10143      * @param {Number} col The column index
10144      * @param {Boolean} hscroll false to disable horizontal scrolling
10145      */
10146     focusCell : function(row, col, hscroll)
10147     {
10148         //Roo.log('GridView.focusCell');
10149         var el = this.ensureVisible(row, col, hscroll);
10150         // not sure what focusEL achives = it's a <a> pos relative 
10151         //this.focusEl.alignTo(el, "tl-tl");
10152         //if(Roo.isGecko){
10153         //    this.focusEl.focus();
10154         //}else{
10155         //    this.focusEl.focus.defer(1, this.focusEl);
10156         //}
10157     },
10158     
10159      /**
10160      * Scrolls the specified cell into view
10161      * @param {Number} row The row index
10162      * @param {Number} col The column index
10163      * @param {Boolean} hscroll false to disable horizontal scrolling
10164      */
10165     ensureVisible : function(row, col, hscroll)
10166     {
10167         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10168         //return null; //disable for testing.
10169         if(typeof row != "number"){
10170             row = row.rowIndex;
10171         }
10172         if(row < 0 && row >= this.ds.getCount()){
10173             return  null;
10174         }
10175         col = (col !== undefined ? col : 0);
10176         var cm = this.cm;
10177         while(cm.isHidden(col)){
10178             col++;
10179         }
10180
10181         var el = this.getCellDom(row, col);
10182         if(!el){
10183             return null;
10184         }
10185         var c = this.bodyEl.dom;
10186
10187         var ctop = parseInt(el.offsetTop, 10);
10188         var cleft = parseInt(el.offsetLeft, 10);
10189         var cbot = ctop + el.offsetHeight;
10190         var cright = cleft + el.offsetWidth;
10191
10192         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10193         var ch = 0; //?? header is not withing the area?
10194         var stop = parseInt(c.scrollTop, 10);
10195         var sleft = parseInt(c.scrollLeft, 10);
10196         var sbot = stop + ch;
10197         var sright = sleft + c.clientWidth;
10198         /*
10199         Roo.log('GridView.ensureVisible:' +
10200                 ' ctop:' + ctop +
10201                 ' c.clientHeight:' + c.clientHeight +
10202                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10203                 ' stop:' + stop +
10204                 ' cbot:' + cbot +
10205                 ' sbot:' + sbot +
10206                 ' ch:' + ch  
10207                 );
10208         */
10209         if(ctop < stop){
10210             c.scrollTop = ctop;
10211             //Roo.log("set scrolltop to ctop DISABLE?");
10212         }else if(cbot > sbot){
10213             //Roo.log("set scrolltop to cbot-ch");
10214             c.scrollTop = cbot-ch;
10215         }
10216
10217         if(hscroll !== false){
10218             if(cleft < sleft){
10219                 c.scrollLeft = cleft;
10220             }else if(cright > sright){
10221                 c.scrollLeft = cright-c.clientWidth;
10222             }
10223         }
10224
10225         return el;
10226     },
10227     
10228     
10229     insertRow : function(dm, rowIndex, isUpdate){
10230         
10231         if(!isUpdate){
10232             this.fireEvent("beforerowsinserted", this, rowIndex);
10233         }
10234             //var s = this.getScrollState();
10235         var row = this.renderRow(this.cm, this.store, rowIndex);
10236         // insert before rowIndex..
10237         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10238         
10239         var _this = this;
10240                 
10241         if(row.cellObjects.length){
10242             Roo.each(row.cellObjects, function(r){
10243                 _this.renderCellObject(r);
10244             })
10245         }
10246             
10247         if(!isUpdate){
10248             this.fireEvent("rowsinserted", this, rowIndex);
10249             //this.syncRowHeights(firstRow, lastRow);
10250             //this.stripeRows(firstRow);
10251             //this.layout();
10252         }
10253         
10254     },
10255     
10256     
10257     getRowDom : function(rowIndex)
10258     {
10259         var rows = this.el.select('tbody > tr', true).elements;
10260         
10261         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10262         
10263     },
10264     getCellDom : function(rowIndex, colIndex)
10265     {
10266         var row = this.getRowDom(rowIndex);
10267         if (row === false) {
10268             return false;
10269         }
10270         var cols = row.select('td', true).elements;
10271         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10272         
10273     },
10274     
10275     // returns the object tree for a tr..
10276   
10277     
10278     renderRow : function(cm, ds, rowIndex) 
10279     {
10280         var d = ds.getAt(rowIndex);
10281         
10282         var row = {
10283             tag : 'tr',
10284             cls : 'x-row-' + rowIndex,
10285             cn : []
10286         };
10287             
10288         var cellObjects = [];
10289         
10290         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10291             var config = cm.config[i];
10292             
10293             var renderer = cm.getRenderer(i);
10294             var value = '';
10295             var id = false;
10296             
10297             if(typeof(renderer) !== 'undefined'){
10298                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10299             }
10300             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10301             // and are rendered into the cells after the row is rendered - using the id for the element.
10302             
10303             if(typeof(value) === 'object'){
10304                 id = Roo.id();
10305                 cellObjects.push({
10306                     container : id,
10307                     cfg : value 
10308                 })
10309             }
10310             
10311             var rowcfg = {
10312                 record: d,
10313                 rowIndex : rowIndex,
10314                 colIndex : i,
10315                 rowClass : ''
10316             };
10317
10318             this.fireEvent('rowclass', this, rowcfg);
10319             
10320             var td = {
10321                 tag: 'td',
10322                 // this might end up displaying HTML?
10323                 // this is too messy... - better to only do it on columsn you know are going to be too long
10324                 //tooltip : (typeof(value) === 'object') ? '' : value,
10325                 cls : rowcfg.rowClass + ' x-col-' + i,
10326                 style: '',
10327                 html: (typeof(value) === 'object') ? '' : value
10328             };
10329             
10330             if (id) {
10331                 td.id = id;
10332             }
10333             
10334             if(typeof(config.colspan) != 'undefined'){
10335                 td.colspan = config.colspan;
10336             }
10337             
10338             
10339             
10340             if(typeof(config.align) != 'undefined' && config.align.length){
10341                 td.style += ' text-align:' + config.align + ';';
10342             }
10343             if(typeof(config.valign) != 'undefined' && config.valign.length){
10344                 td.style += ' vertical-align:' + config.valign + ';';
10345             }
10346             /*
10347             if(typeof(config.width) != 'undefined'){
10348                 td.style += ' width:' +  config.width + 'px;';
10349             }
10350             */
10351             
10352             if(typeof(config.cursor) != 'undefined'){
10353                 td.style += ' cursor:' +  config.cursor + ';';
10354             }
10355             
10356             if(typeof(config.cls) != 'undefined'){
10357                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10358             }
10359             if (this.responsive) {
10360                 ['xs','sm','md','lg'].map(function(size){
10361                     
10362                     if(typeof(config[size]) == 'undefined'){
10363                         return;
10364                     }
10365                     
10366                     
10367                       
10368                     if (!config[size]) { // 0 = hidden
10369                         // BS 4 '0' is treated as hide that column and below.
10370                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10371                         return;
10372                     }
10373                     
10374                     td.cls += ' col-' + size + '-' + config[size] + (
10375                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10376                     );
10377                      
10378     
10379                 });
10380             }
10381             row.cn.push(td);
10382            
10383         }
10384         
10385         row.cellObjects = cellObjects;
10386         
10387         return row;
10388           
10389     },
10390     
10391     
10392     
10393     onBeforeLoad : function()
10394     {
10395         
10396     },
10397      /**
10398      * Remove all rows
10399      */
10400     clear : function()
10401     {
10402         this.el.select('tbody', true).first().dom.innerHTML = '';
10403     },
10404     /**
10405      * Show or hide a row.
10406      * @param {Number} rowIndex to show or hide
10407      * @param {Boolean} state hide
10408      */
10409     setRowVisibility : function(rowIndex, state)
10410     {
10411         var bt = this.bodyEl.dom;
10412         
10413         var rows = this.el.select('tbody > tr', true).elements;
10414         
10415         if(typeof(rows[rowIndex]) == 'undefined'){
10416             return;
10417         }
10418         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10419         
10420     },
10421     
10422     
10423     getSelectionModel : function(){
10424         if(!this.selModel){
10425             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10426         }
10427         return this.selModel;
10428     },
10429     /*
10430      * Render the Roo.bootstrap object from renderder
10431      */
10432     renderCellObject : function(r)
10433     {
10434         var _this = this;
10435         
10436         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10437         
10438         var t = r.cfg.render(r.container);
10439         
10440         if(r.cfg.cn){
10441             Roo.each(r.cfg.cn, function(c){
10442                 var child = {
10443                     container: t.getChildContainer(),
10444                     cfg: c
10445                 };
10446                 _this.renderCellObject(child);
10447             })
10448         }
10449     },
10450     /**
10451      * get the Row Index from a dom element.
10452      * @param {Roo.Element} row The row to look for
10453      * @returns {Number} the row
10454      */
10455     getRowIndex : function(row)
10456     {
10457         var rowIndex = -1;
10458         
10459         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10460             if(el != row){
10461                 return;
10462             }
10463             
10464             rowIndex = index;
10465         });
10466         
10467         return rowIndex;
10468     },
10469     /**
10470      * get the header TH element for columnIndex
10471      * @param {Number} columnIndex
10472      * @returns {Roo.Element}
10473      */
10474     getHeaderIndex: function(colIndex)
10475     {
10476         var cols = this.headEl.select('th', true).elements;
10477         return cols[colIndex]; 
10478     },
10479     /**
10480      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10481      * @param {domElement} cell to look for
10482      * @returns {Number} the column
10483      */
10484     getCellIndex : function(cell)
10485     {
10486         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10487         if(id){
10488             return parseInt(id[1], 10);
10489         }
10490         return 0;
10491     },
10492      /**
10493      * Returns the grid's underlying element = used by panel.Grid
10494      * @return {Element} The element
10495      */
10496     getGridEl : function(){
10497         return this.el;
10498     },
10499      /**
10500      * Forces a resize - used by panel.Grid
10501      * @return {Element} The element
10502      */
10503     autoSize : function()
10504     {
10505         //var ctr = Roo.get(this.container.dom.parentElement);
10506         var ctr = Roo.get(this.el.dom);
10507         
10508         var thd = this.getGridEl().select('thead',true).first();
10509         var tbd = this.getGridEl().select('tbody', true).first();
10510         var tfd = this.getGridEl().select('tfoot', true).first();
10511         
10512         var cw = ctr.getWidth();
10513         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10514         
10515         if (tbd) {
10516             
10517             tbd.setWidth(ctr.getWidth());
10518             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10519             // this needs fixing for various usage - currently only hydra job advers I think..
10520             //tdb.setHeight(
10521             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10522             //); 
10523             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10524             cw -= barsize;
10525         }
10526         cw = Math.max(cw, this.totalWidth);
10527         this.getGridEl().select('tbody tr',true).setWidth(cw);
10528         this.initCSS();
10529         
10530         // resize 'expandable coloumn?
10531         
10532         return; // we doe not have a view in this design..
10533         
10534     },
10535     onBodyScroll: function()
10536     {
10537         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10538         if(this.headEl){
10539             this.headEl.setStyle({
10540                 'position' : 'relative',
10541                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10542             });
10543         }
10544         
10545         if(this.lazyLoad){
10546             
10547             var scrollHeight = this.bodyEl.dom.scrollHeight;
10548             
10549             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10550             
10551             var height = this.bodyEl.getHeight();
10552             
10553             if(scrollHeight - height == scrollTop) {
10554                 
10555                 var total = this.ds.getTotalCount();
10556                 
10557                 if(this.footer.cursor + this.footer.pageSize < total){
10558                     
10559                     this.footer.ds.load({
10560                         params : {
10561                             start : this.footer.cursor + this.footer.pageSize,
10562                             limit : this.footer.pageSize
10563                         },
10564                         add : true
10565                     });
10566                 }
10567             }
10568             
10569         }
10570     },
10571     onColumnSplitterMoved : function(i, diff)
10572     {
10573         this.userResized = true;
10574         
10575         var cm = this.colModel;
10576         
10577         var w = this.getHeaderIndex(i).getWidth() + diff;
10578         
10579         
10580         cm.setColumnWidth(i, w, true);
10581         this.initCSS();
10582         //var cid = cm.getColumnId(i); << not used in this version?
10583        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10584         
10585         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10586         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10587         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10588 */
10589         //this.updateSplitters();
10590         //this.layout(); << ??
10591         this.fireEvent("columnresize", i, w);
10592     },
10593     onHeaderChange : function()
10594     {
10595         var header = this.renderHeader();
10596         var table = this.el.select('table', true).first();
10597         
10598         this.headEl.remove();
10599         this.headEl = table.createChild(header, this.bodyEl, false);
10600         
10601         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10602             e.on('click', this.sort, this);
10603         }, this);
10604         
10605         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10606             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10607         }
10608         
10609     },
10610     
10611     onHiddenChange : function(colModel, colIndex, hidden)
10612     {
10613         /*
10614         this.cm.setHidden()
10615         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10616         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10617         
10618         this.CSS.updateRule(thSelector, "display", "");
10619         this.CSS.updateRule(tdSelector, "display", "");
10620         
10621         if(hidden){
10622             this.CSS.updateRule(thSelector, "display", "none");
10623             this.CSS.updateRule(tdSelector, "display", "none");
10624         }
10625         */
10626         // onload calls initCSS()
10627         this.onHeaderChange();
10628         this.onLoad();
10629     },
10630     
10631     setColumnWidth: function(col_index, width)
10632     {
10633         // width = "md-2 xs-2..."
10634         if(!this.colModel.config[col_index]) {
10635             return;
10636         }
10637         
10638         var w = width.split(" ");
10639         
10640         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10641         
10642         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10643         
10644         
10645         for(var j = 0; j < w.length; j++) {
10646             
10647             if(!w[j]) {
10648                 continue;
10649             }
10650             
10651             var size_cls = w[j].split("-");
10652             
10653             if(!Number.isInteger(size_cls[1] * 1)) {
10654                 continue;
10655             }
10656             
10657             if(!this.colModel.config[col_index][size_cls[0]]) {
10658                 continue;
10659             }
10660             
10661             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10662                 continue;
10663             }
10664             
10665             h_row[0].classList.replace(
10666                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10667                 "col-"+size_cls[0]+"-"+size_cls[1]
10668             );
10669             
10670             for(var i = 0; i < rows.length; i++) {
10671                 
10672                 var size_cls = w[j].split("-");
10673                 
10674                 if(!Number.isInteger(size_cls[1] * 1)) {
10675                     continue;
10676                 }
10677                 
10678                 if(!this.colModel.config[col_index][size_cls[0]]) {
10679                     continue;
10680                 }
10681                 
10682                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10683                     continue;
10684                 }
10685                 
10686                 rows[i].classList.replace(
10687                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10688                     "col-"+size_cls[0]+"-"+size_cls[1]
10689                 );
10690             }
10691             
10692             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10693         }
10694     }
10695 });
10696
10697 // currently only used to find the split on drag.. 
10698 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10699
10700 /**
10701  * @depricated
10702 */
10703 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10704 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10705 /*
10706  * - LGPL
10707  *
10708  * table cell
10709  * 
10710  */
10711
10712 /**
10713  * @class Roo.bootstrap.TableCell
10714  * @extends Roo.bootstrap.Component
10715  * @children Roo.bootstrap.Component
10716  * @parent Roo.bootstrap.TableRow
10717  * Bootstrap TableCell class
10718  * 
10719  * @cfg {String} html cell contain text
10720  * @cfg {String} cls cell class
10721  * @cfg {String} tag cell tag (td|th) default td
10722  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10723  * @cfg {String} align Aligns the content in a cell
10724  * @cfg {String} axis Categorizes cells
10725  * @cfg {String} bgcolor Specifies the background color of a cell
10726  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10727  * @cfg {Number} colspan Specifies the number of columns a cell should span
10728  * @cfg {String} headers Specifies one or more header cells a cell is related to
10729  * @cfg {Number} height Sets the height of a cell
10730  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10731  * @cfg {Number} rowspan Sets the number of rows a cell should span
10732  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10733  * @cfg {String} valign Vertical aligns the content in a cell
10734  * @cfg {Number} width Specifies the width of a cell
10735  * 
10736  * @constructor
10737  * Create a new TableCell
10738  * @param {Object} config The config object
10739  */
10740
10741 Roo.bootstrap.TableCell = function(config){
10742     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10743 };
10744
10745 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10746     
10747     html: false,
10748     cls: false,
10749     tag: false,
10750     abbr: false,
10751     align: false,
10752     axis: false,
10753     bgcolor: false,
10754     charoff: false,
10755     colspan: false,
10756     headers: false,
10757     height: false,
10758     nowrap: false,
10759     rowspan: false,
10760     scope: false,
10761     valign: false,
10762     width: false,
10763     
10764     
10765     getAutoCreate : function(){
10766         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10767         
10768         cfg = {
10769             tag: 'td'
10770         };
10771         
10772         if(this.tag){
10773             cfg.tag = this.tag;
10774         }
10775         
10776         if (this.html) {
10777             cfg.html=this.html
10778         }
10779         if (this.cls) {
10780             cfg.cls=this.cls
10781         }
10782         if (this.abbr) {
10783             cfg.abbr=this.abbr
10784         }
10785         if (this.align) {
10786             cfg.align=this.align
10787         }
10788         if (this.axis) {
10789             cfg.axis=this.axis
10790         }
10791         if (this.bgcolor) {
10792             cfg.bgcolor=this.bgcolor
10793         }
10794         if (this.charoff) {
10795             cfg.charoff=this.charoff
10796         }
10797         if (this.colspan) {
10798             cfg.colspan=this.colspan
10799         }
10800         if (this.headers) {
10801             cfg.headers=this.headers
10802         }
10803         if (this.height) {
10804             cfg.height=this.height
10805         }
10806         if (this.nowrap) {
10807             cfg.nowrap=this.nowrap
10808         }
10809         if (this.rowspan) {
10810             cfg.rowspan=this.rowspan
10811         }
10812         if (this.scope) {
10813             cfg.scope=this.scope
10814         }
10815         if (this.valign) {
10816             cfg.valign=this.valign
10817         }
10818         if (this.width) {
10819             cfg.width=this.width
10820         }
10821         
10822         
10823         return cfg;
10824     }
10825    
10826 });
10827
10828  
10829
10830  /*
10831  * - LGPL
10832  *
10833  * table row
10834  * 
10835  */
10836
10837 /**
10838  * @class Roo.bootstrap.TableRow
10839  * @extends Roo.bootstrap.Component
10840  * @children Roo.bootstrap.TableCell
10841  * @parent Roo.bootstrap.TableBody
10842  * Bootstrap TableRow class
10843  * @cfg {String} cls row class
10844  * @cfg {String} align Aligns the content in a table row
10845  * @cfg {String} bgcolor Specifies a background color for a table row
10846  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10847  * @cfg {String} valign Vertical aligns the content in a table row
10848  * 
10849  * @constructor
10850  * Create a new TableRow
10851  * @param {Object} config The config object
10852  */
10853
10854 Roo.bootstrap.TableRow = function(config){
10855     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10856 };
10857
10858 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10859     
10860     cls: false,
10861     align: false,
10862     bgcolor: false,
10863     charoff: false,
10864     valign: false,
10865     
10866     getAutoCreate : function(){
10867         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10868         
10869         cfg = {
10870             tag: 'tr'
10871         };
10872             
10873         if(this.cls){
10874             cfg.cls = this.cls;
10875         }
10876         if(this.align){
10877             cfg.align = this.align;
10878         }
10879         if(this.bgcolor){
10880             cfg.bgcolor = this.bgcolor;
10881         }
10882         if(this.charoff){
10883             cfg.charoff = this.charoff;
10884         }
10885         if(this.valign){
10886             cfg.valign = this.valign;
10887         }
10888         
10889         return cfg;
10890     }
10891    
10892 });
10893
10894  
10895
10896  /*
10897  * - LGPL
10898  *
10899  * table body
10900  * 
10901  */
10902
10903 /**
10904  * @class Roo.bootstrap.TableBody
10905  * @extends Roo.bootstrap.Component
10906  * @children Roo.bootstrap.TableRow
10907  * @parent Roo.bootstrap.Table
10908  * Bootstrap TableBody class
10909  * @cfg {String} cls element class
10910  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10911  * @cfg {String} align Aligns the content inside the element
10912  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10913  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10914  * 
10915  * @constructor
10916  * Create a new TableBody
10917  * @param {Object} config The config object
10918  */
10919
10920 Roo.bootstrap.TableBody = function(config){
10921     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10922 };
10923
10924 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10925     
10926     cls: false,
10927     tag: false,
10928     align: false,
10929     charoff: false,
10930     valign: false,
10931     
10932     getAutoCreate : function(){
10933         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10934         
10935         cfg = {
10936             tag: 'tbody'
10937         };
10938             
10939         if (this.cls) {
10940             cfg.cls=this.cls
10941         }
10942         if(this.tag){
10943             cfg.tag = this.tag;
10944         }
10945         
10946         if(this.align){
10947             cfg.align = this.align;
10948         }
10949         if(this.charoff){
10950             cfg.charoff = this.charoff;
10951         }
10952         if(this.valign){
10953             cfg.valign = this.valign;
10954         }
10955         
10956         return cfg;
10957     }
10958     
10959     
10960 //    initEvents : function()
10961 //    {
10962 //        
10963 //        if(!this.store){
10964 //            return;
10965 //        }
10966 //        
10967 //        this.store = Roo.factory(this.store, Roo.data);
10968 //        this.store.on('load', this.onLoad, this);
10969 //        
10970 //        this.store.load();
10971 //        
10972 //    },
10973 //    
10974 //    onLoad: function () 
10975 //    {   
10976 //        this.fireEvent('load', this);
10977 //    }
10978 //    
10979 //   
10980 });
10981
10982  
10983
10984  /*
10985  * Based on:
10986  * Ext JS Library 1.1.1
10987  * Copyright(c) 2006-2007, Ext JS, LLC.
10988  *
10989  * Originally Released Under LGPL - original licence link has changed is not relivant.
10990  *
10991  * Fork - LGPL
10992  * <script type="text/javascript">
10993  */
10994
10995 // as we use this in bootstrap.
10996 Roo.namespace('Roo.form');
10997  /**
10998  * @class Roo.form.Action
10999  * Internal Class used to handle form actions
11000  * @constructor
11001  * @param {Roo.form.BasicForm} el The form element or its id
11002  * @param {Object} config Configuration options
11003  */
11004
11005  
11006  
11007 // define the action interface
11008 Roo.form.Action = function(form, options){
11009     this.form = form;
11010     this.options = options || {};
11011 };
11012 /**
11013  * Client Validation Failed
11014  * @const 
11015  */
11016 Roo.form.Action.CLIENT_INVALID = 'client';
11017 /**
11018  * Server Validation Failed
11019  * @const 
11020  */
11021 Roo.form.Action.SERVER_INVALID = 'server';
11022  /**
11023  * Connect to Server Failed
11024  * @const 
11025  */
11026 Roo.form.Action.CONNECT_FAILURE = 'connect';
11027 /**
11028  * Reading Data from Server Failed
11029  * @const 
11030  */
11031 Roo.form.Action.LOAD_FAILURE = 'load';
11032
11033 Roo.form.Action.prototype = {
11034     type : 'default',
11035     failureType : undefined,
11036     response : undefined,
11037     result : undefined,
11038
11039     // interface method
11040     run : function(options){
11041
11042     },
11043
11044     // interface method
11045     success : function(response){
11046
11047     },
11048
11049     // interface method
11050     handleResponse : function(response){
11051
11052     },
11053
11054     // default connection failure
11055     failure : function(response){
11056         
11057         this.response = response;
11058         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11059         this.form.afterAction(this, false);
11060     },
11061
11062     processResponse : function(response){
11063         this.response = response;
11064         if(!response.responseText){
11065             return true;
11066         }
11067         this.result = this.handleResponse(response);
11068         return this.result;
11069     },
11070
11071     // utility functions used internally
11072     getUrl : function(appendParams){
11073         var url = this.options.url || this.form.url || this.form.el.dom.action;
11074         if(appendParams){
11075             var p = this.getParams();
11076             if(p){
11077                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11078             }
11079         }
11080         return url;
11081     },
11082
11083     getMethod : function(){
11084         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11085     },
11086
11087     getParams : function(){
11088         var bp = this.form.baseParams;
11089         var p = this.options.params;
11090         if(p){
11091             if(typeof p == "object"){
11092                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11093             }else if(typeof p == 'string' && bp){
11094                 p += '&' + Roo.urlEncode(bp);
11095             }
11096         }else if(bp){
11097             p = Roo.urlEncode(bp);
11098         }
11099         return p;
11100     },
11101
11102     createCallback : function(){
11103         return {
11104             success: this.success,
11105             failure: this.failure,
11106             scope: this,
11107             timeout: (this.form.timeout*1000),
11108             upload: this.form.fileUpload ? this.success : undefined
11109         };
11110     }
11111 };
11112
11113 Roo.form.Action.Submit = function(form, options){
11114     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11115 };
11116
11117 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11118     type : 'submit',
11119
11120     haveProgress : false,
11121     uploadComplete : false,
11122     
11123     // uploadProgress indicator.
11124     uploadProgress : function()
11125     {
11126         if (!this.form.progressUrl) {
11127             return;
11128         }
11129         
11130         if (!this.haveProgress) {
11131             Roo.MessageBox.progress("Uploading", "Uploading");
11132         }
11133         if (this.uploadComplete) {
11134            Roo.MessageBox.hide();
11135            return;
11136         }
11137         
11138         this.haveProgress = true;
11139    
11140         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11141         
11142         var c = new Roo.data.Connection();
11143         c.request({
11144             url : this.form.progressUrl,
11145             params: {
11146                 id : uid
11147             },
11148             method: 'GET',
11149             success : function(req){
11150                //console.log(data);
11151                 var rdata = false;
11152                 var edata;
11153                 try  {
11154                    rdata = Roo.decode(req.responseText)
11155                 } catch (e) {
11156                     Roo.log("Invalid data from server..");
11157                     Roo.log(edata);
11158                     return;
11159                 }
11160                 if (!rdata || !rdata.success) {
11161                     Roo.log(rdata);
11162                     Roo.MessageBox.alert(Roo.encode(rdata));
11163                     return;
11164                 }
11165                 var data = rdata.data;
11166                 
11167                 if (this.uploadComplete) {
11168                    Roo.MessageBox.hide();
11169                    return;
11170                 }
11171                    
11172                 if (data){
11173                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11174                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11175                     );
11176                 }
11177                 this.uploadProgress.defer(2000,this);
11178             },
11179        
11180             failure: function(data) {
11181                 Roo.log('progress url failed ');
11182                 Roo.log(data);
11183             },
11184             scope : this
11185         });
11186            
11187     },
11188     
11189     
11190     run : function()
11191     {
11192         // run get Values on the form, so it syncs any secondary forms.
11193         this.form.getValues();
11194         
11195         var o = this.options;
11196         var method = this.getMethod();
11197         var isPost = method == 'POST';
11198         if(o.clientValidation === false || this.form.isValid()){
11199             
11200             if (this.form.progressUrl) {
11201                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11202                     (new Date() * 1) + '' + Math.random());
11203                     
11204             } 
11205             
11206             
11207             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11208                 form:this.form.el.dom,
11209                 url:this.getUrl(!isPost),
11210                 method: method,
11211                 params:isPost ? this.getParams() : null,
11212                 isUpload: this.form.fileUpload,
11213                 formData : this.form.formData
11214             }));
11215             
11216             this.uploadProgress();
11217
11218         }else if (o.clientValidation !== false){ // client validation failed
11219             this.failureType = Roo.form.Action.CLIENT_INVALID;
11220             this.form.afterAction(this, false);
11221         }
11222     },
11223
11224     success : function(response)
11225     {
11226         this.uploadComplete= true;
11227         if (this.haveProgress) {
11228             Roo.MessageBox.hide();
11229         }
11230         
11231         
11232         var result = this.processResponse(response);
11233         if(result === true || result.success){
11234             this.form.afterAction(this, true);
11235             return;
11236         }
11237         if(result.errors){
11238             this.form.markInvalid(result.errors);
11239             this.failureType = Roo.form.Action.SERVER_INVALID;
11240         }
11241         this.form.afterAction(this, false);
11242     },
11243     failure : function(response)
11244     {
11245         this.uploadComplete= true;
11246         if (this.haveProgress) {
11247             Roo.MessageBox.hide();
11248         }
11249         
11250         this.response = response;
11251         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11252         this.form.afterAction(this, false);
11253     },
11254     
11255     handleResponse : function(response){
11256         if(this.form.errorReader){
11257             var rs = this.form.errorReader.read(response);
11258             var errors = [];
11259             if(rs.records){
11260                 for(var i = 0, len = rs.records.length; i < len; i++) {
11261                     var r = rs.records[i];
11262                     errors[i] = r.data;
11263                 }
11264             }
11265             if(errors.length < 1){
11266                 errors = null;
11267             }
11268             return {
11269                 success : rs.success,
11270                 errors : errors
11271             };
11272         }
11273         var ret = false;
11274         try {
11275             ret = Roo.decode(response.responseText);
11276         } catch (e) {
11277             ret = {
11278                 success: false,
11279                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11280                 errors : []
11281             };
11282         }
11283         return ret;
11284         
11285     }
11286 });
11287
11288
11289 Roo.form.Action.Load = function(form, options){
11290     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11291     this.reader = this.form.reader;
11292 };
11293
11294 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11295     type : 'load',
11296
11297     run : function(){
11298         
11299         Roo.Ajax.request(Roo.apply(
11300                 this.createCallback(), {
11301                     method:this.getMethod(),
11302                     url:this.getUrl(false),
11303                     params:this.getParams()
11304         }));
11305     },
11306
11307     success : function(response){
11308         
11309         var result = this.processResponse(response);
11310         if(result === true || !result.success || !result.data){
11311             this.failureType = Roo.form.Action.LOAD_FAILURE;
11312             this.form.afterAction(this, false);
11313             return;
11314         }
11315         this.form.clearInvalid();
11316         this.form.setValues(result.data);
11317         this.form.afterAction(this, true);
11318     },
11319
11320     handleResponse : function(response){
11321         if(this.form.reader){
11322             var rs = this.form.reader.read(response);
11323             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11324             return {
11325                 success : rs.success,
11326                 data : data
11327             };
11328         }
11329         return Roo.decode(response.responseText);
11330     }
11331 });
11332
11333 Roo.form.Action.ACTION_TYPES = {
11334     'load' : Roo.form.Action.Load,
11335     'submit' : Roo.form.Action.Submit
11336 };/*
11337  * - LGPL
11338  *
11339  * form
11340  *
11341  */
11342
11343 /**
11344  * @class Roo.bootstrap.Form
11345  * @extends Roo.bootstrap.Component
11346  * @children Roo.bootstrap.Component
11347  * Bootstrap Form class
11348  * @cfg {String} method  GET | POST (default POST)
11349  * @cfg {String} labelAlign top | left (default top)
11350  * @cfg {String} align left  | right - for navbars
11351  * @cfg {Boolean} loadMask load mask when submit (default true)
11352
11353  *
11354  * @constructor
11355  * Create a new Form
11356  * @param {Object} config The config object
11357  */
11358
11359
11360 Roo.bootstrap.Form = function(config){
11361     
11362     Roo.bootstrap.Form.superclass.constructor.call(this, config);
11363     
11364     Roo.bootstrap.Form.popover.apply();
11365     
11366     this.addEvents({
11367         /**
11368          * @event clientvalidation
11369          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11370          * @param {Form} this
11371          * @param {Boolean} valid true if the form has passed client-side validation
11372          */
11373         clientvalidation: true,
11374         /**
11375          * @event beforeaction
11376          * Fires before any action is performed. Return false to cancel the action.
11377          * @param {Form} this
11378          * @param {Action} action The action to be performed
11379          */
11380         beforeaction: true,
11381         /**
11382          * @event actionfailed
11383          * Fires when an action fails.
11384          * @param {Form} this
11385          * @param {Action} action The action that failed
11386          */
11387         actionfailed : true,
11388         /**
11389          * @event actioncomplete
11390          * Fires when an action is completed.
11391          * @param {Form} this
11392          * @param {Action} action The action that completed
11393          */
11394         actioncomplete : true
11395     });
11396 };
11397
11398 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
11399
11400      /**
11401      * @cfg {String} method
11402      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11403      */
11404     method : 'POST',
11405     /**
11406      * @cfg {String} url
11407      * The URL to use for form actions if one isn't supplied in the action options.
11408      */
11409     /**
11410      * @cfg {Boolean} fileUpload
11411      * Set to true if this form is a file upload.
11412      */
11413
11414     /**
11415      * @cfg {Object} baseParams
11416      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11417      */
11418
11419     /**
11420      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11421      */
11422     timeout: 30,
11423     /**
11424      * @cfg {Sting} align (left|right) for navbar forms
11425      */
11426     align : 'left',
11427
11428     // private
11429     activeAction : null,
11430
11431     /**
11432      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11433      * element by passing it or its id or mask the form itself by passing in true.
11434      * @type Mixed
11435      */
11436     waitMsgTarget : false,
11437
11438     loadMask : true,
11439     
11440     /**
11441      * @cfg {Boolean} errorMask (true|false) default false
11442      */
11443     errorMask : false,
11444     
11445     /**
11446      * @cfg {Number} maskOffset Default 100
11447      */
11448     maskOffset : 100,
11449     
11450     /**
11451      * @cfg {Boolean} maskBody
11452      */
11453     maskBody : false,
11454
11455     getAutoCreate : function(){
11456
11457         var cfg = {
11458             tag: 'form',
11459             method : this.method || 'POST',
11460             id : this.id || Roo.id(),
11461             cls : ''
11462         };
11463         if (this.parent().xtype.match(/^Nav/)) {
11464             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11465
11466         }
11467
11468         if (this.labelAlign == 'left' ) {
11469             cfg.cls += ' form-horizontal';
11470         }
11471
11472
11473         return cfg;
11474     },
11475     initEvents : function()
11476     {
11477         this.el.on('submit', this.onSubmit, this);
11478         // this was added as random key presses on the form where triggering form submit.
11479         this.el.on('keypress', function(e) {
11480             if (e.getCharCode() != 13) {
11481                 return true;
11482             }
11483             // we might need to allow it for textareas.. and some other items.
11484             // check e.getTarget().
11485
11486             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11487                 return true;
11488             }
11489
11490             Roo.log("keypress blocked");
11491
11492             e.preventDefault();
11493             return false;
11494         });
11495         
11496     },
11497     // private
11498     onSubmit : function(e){
11499         e.stopEvent();
11500     },
11501
11502      /**
11503      * Returns true if client-side validation on the form is successful.
11504      * @return Boolean
11505      */
11506     isValid : function(){
11507         var items = this.getItems();
11508         var valid = true;
11509         var target = false;
11510         
11511         items.each(function(f){
11512             
11513             if(f.validate()){
11514                 return;
11515             }
11516             
11517             Roo.log('invalid field: ' + f.name);
11518             
11519             valid = false;
11520
11521             if(!target && f.el.isVisible(true)){
11522                 target = f;
11523             }
11524            
11525         });
11526         
11527         if(this.errorMask && !valid){
11528             Roo.bootstrap.Form.popover.mask(this, target);
11529         }
11530         
11531         return valid;
11532     },
11533     
11534     /**
11535      * Returns true if any fields in this form have changed since their original load.
11536      * @return Boolean
11537      */
11538     isDirty : function(){
11539         var dirty = false;
11540         var items = this.getItems();
11541         items.each(function(f){
11542            if(f.isDirty()){
11543                dirty = true;
11544                return false;
11545            }
11546            return true;
11547         });
11548         return dirty;
11549     },
11550      /**
11551      * Performs a predefined action (submit or load) or custom actions you define on this form.
11552      * @param {String} actionName The name of the action type
11553      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11554      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11555      * accept other config options):
11556      * <pre>
11557 Property          Type             Description
11558 ----------------  ---------------  ----------------------------------------------------------------------------------
11559 url               String           The url for the action (defaults to the form's url)
11560 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11561 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11562 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11563                                    validate the form on the client (defaults to false)
11564      * </pre>
11565      * @return {BasicForm} this
11566      */
11567     doAction : function(action, options){
11568         if(typeof action == 'string'){
11569             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11570         }
11571         if(this.fireEvent('beforeaction', this, action) !== false){
11572             this.beforeAction(action);
11573             action.run.defer(100, action);
11574         }
11575         return this;
11576     },
11577
11578     // private
11579     beforeAction : function(action){
11580         var o = action.options;
11581         
11582         if(this.loadMask){
11583             
11584             if(this.maskBody){
11585                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11586             } else {
11587                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11588             }
11589         }
11590         // not really supported yet.. ??
11591
11592         //if(this.waitMsgTarget === true){
11593         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11594         //}else if(this.waitMsgTarget){
11595         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11596         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11597         //}else {
11598         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11599        // }
11600
11601     },
11602
11603     // private
11604     afterAction : function(action, success){
11605         this.activeAction = null;
11606         var o = action.options;
11607
11608         if(this.loadMask){
11609             
11610             if(this.maskBody){
11611                 Roo.get(document.body).unmask();
11612             } else {
11613                 this.el.unmask();
11614             }
11615         }
11616         
11617         //if(this.waitMsgTarget === true){
11618 //            this.el.unmask();
11619         //}else if(this.waitMsgTarget){
11620         //    this.waitMsgTarget.unmask();
11621         //}else{
11622         //    Roo.MessageBox.updateProgress(1);
11623         //    Roo.MessageBox.hide();
11624        // }
11625         //
11626         if(success){
11627             if(o.reset){
11628                 this.reset();
11629             }
11630             Roo.callback(o.success, o.scope, [this, action]);
11631             this.fireEvent('actioncomplete', this, action);
11632
11633         }else{
11634
11635             // failure condition..
11636             // we have a scenario where updates need confirming.
11637             // eg. if a locking scenario exists..
11638             // we look for { errors : { needs_confirm : true }} in the response.
11639             if (
11640                 (typeof(action.result) != 'undefined')  &&
11641                 (typeof(action.result.errors) != 'undefined')  &&
11642                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11643            ){
11644                 var _t = this;
11645                 Roo.log("not supported yet");
11646                  /*
11647
11648                 Roo.MessageBox.confirm(
11649                     "Change requires confirmation",
11650                     action.result.errorMsg,
11651                     function(r) {
11652                         if (r != 'yes') {
11653                             return;
11654                         }
11655                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11656                     }
11657
11658                 );
11659                 */
11660
11661
11662                 return;
11663             }
11664
11665             Roo.callback(o.failure, o.scope, [this, action]);
11666             // show an error message if no failed handler is set..
11667             if (!this.hasListener('actionfailed')) {
11668                 Roo.log("need to add dialog support");
11669                 /*
11670                 Roo.MessageBox.alert("Error",
11671                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11672                         action.result.errorMsg :
11673                         "Saving Failed, please check your entries or try again"
11674                 );
11675                 */
11676             }
11677
11678             this.fireEvent('actionfailed', this, action);
11679         }
11680
11681     },
11682     /**
11683      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11684      * @param {String} id The value to search for
11685      * @return Field
11686      */
11687     findField : function(id){
11688         var items = this.getItems();
11689         var field = items.get(id);
11690         if(!field){
11691              items.each(function(f){
11692                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11693                     field = f;
11694                     return false;
11695                 }
11696                 return true;
11697             });
11698         }
11699         return field || null;
11700     },
11701      /**
11702      * Mark fields in this form invalid in bulk.
11703      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11704      * @return {BasicForm} this
11705      */
11706     markInvalid : function(errors){
11707         if(errors instanceof Array){
11708             for(var i = 0, len = errors.length; i < len; i++){
11709                 var fieldError = errors[i];
11710                 var f = this.findField(fieldError.id);
11711                 if(f){
11712                     f.markInvalid(fieldError.msg);
11713                 }
11714             }
11715         }else{
11716             var field, id;
11717             for(id in errors){
11718                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11719                     field.markInvalid(errors[id]);
11720                 }
11721             }
11722         }
11723         //Roo.each(this.childForms || [], function (f) {
11724         //    f.markInvalid(errors);
11725         //});
11726
11727         return this;
11728     },
11729
11730     /**
11731      * Set values for fields in this form in bulk.
11732      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11733      * @return {BasicForm} this
11734      */
11735     setValues : function(values){
11736         if(values instanceof Array){ // array of objects
11737             for(var i = 0, len = values.length; i < len; i++){
11738                 var v = values[i];
11739                 var f = this.findField(v.id);
11740                 if(f){
11741                     f.setValue(v.value);
11742                     if(this.trackResetOnLoad){
11743                         f.originalValue = f.getValue();
11744                     }
11745                 }
11746             }
11747         }else{ // object hash
11748             var field, id;
11749             for(id in values){
11750                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11751
11752                     if (field.setFromData &&
11753                         field.valueField &&
11754                         field.displayField &&
11755                         // combos' with local stores can
11756                         // be queried via setValue()
11757                         // to set their value..
11758                         (field.store && !field.store.isLocal)
11759                         ) {
11760                         // it's a combo
11761                         var sd = { };
11762                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11763                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11764                         field.setFromData(sd);
11765
11766                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11767                         
11768                         field.setFromData(values);
11769                         
11770                     } else {
11771                         field.setValue(values[id]);
11772                     }
11773
11774
11775                     if(this.trackResetOnLoad){
11776                         field.originalValue = field.getValue();
11777                     }
11778                 }
11779             }
11780         }
11781
11782         //Roo.each(this.childForms || [], function (f) {
11783         //    f.setValues(values);
11784         //});
11785
11786         return this;
11787     },
11788
11789     /**
11790      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11791      * they are returned as an array.
11792      * @param {Boolean} asString
11793      * @return {Object}
11794      */
11795     getValues : function(asString){
11796         //if (this.childForms) {
11797             // copy values from the child forms
11798         //    Roo.each(this.childForms, function (f) {
11799         //        this.setValues(f.getValues());
11800         //    }, this);
11801         //}
11802
11803
11804
11805         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11806         if(asString === true){
11807             return fs;
11808         }
11809         return Roo.urlDecode(fs);
11810     },
11811
11812     /**
11813      * Returns the fields in this form as an object with key/value pairs.
11814      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11815      * @return {Object}
11816      */
11817     getFieldValues : function(with_hidden)
11818     {
11819         var items = this.getItems();
11820         var ret = {};
11821         items.each(function(f){
11822             
11823             if (!f.getName()) {
11824                 return;
11825             }
11826             
11827             var v = f.getValue();
11828             
11829             if (f.inputType =='radio') {
11830                 if (typeof(ret[f.getName()]) == 'undefined') {
11831                     ret[f.getName()] = ''; // empty..
11832                 }
11833
11834                 if (!f.el.dom.checked) {
11835                     return;
11836
11837                 }
11838                 v = f.el.dom.value;
11839
11840             }
11841             
11842             if(f.xtype == 'MoneyField'){
11843                 ret[f.currencyName] = f.getCurrency();
11844             }
11845
11846             // not sure if this supported any more..
11847             if ((typeof(v) == 'object') && f.getRawValue) {
11848                 v = f.getRawValue() ; // dates..
11849             }
11850             // combo boxes where name != hiddenName...
11851             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11852                 ret[f.name] = f.getRawValue();
11853             }
11854             ret[f.getName()] = v;
11855         });
11856
11857         return ret;
11858     },
11859
11860     /**
11861      * Clears all invalid messages in this form.
11862      * @return {BasicForm} this
11863      */
11864     clearInvalid : function(){
11865         var items = this.getItems();
11866
11867         items.each(function(f){
11868            f.clearInvalid();
11869         });
11870
11871         return this;
11872     },
11873
11874     /**
11875      * Resets this form.
11876      * @return {BasicForm} this
11877      */
11878     reset : function(){
11879         var items = this.getItems();
11880         items.each(function(f){
11881             f.reset();
11882         });
11883
11884         Roo.each(this.childForms || [], function (f) {
11885             f.reset();
11886         });
11887
11888
11889         return this;
11890     },
11891     
11892     getItems : function()
11893     {
11894         var r=new Roo.util.MixedCollection(false, function(o){
11895             return o.id || (o.id = Roo.id());
11896         });
11897         var iter = function(el) {
11898             if (el.inputEl) {
11899                 r.add(el);
11900             }
11901             if (!el.items) {
11902                 return;
11903             }
11904             Roo.each(el.items,function(e) {
11905                 iter(e);
11906             });
11907         };
11908
11909         iter(this);
11910         return r;
11911     },
11912     
11913     hideFields : function(items)
11914     {
11915         Roo.each(items, function(i){
11916             
11917             var f = this.findField(i);
11918             
11919             if(!f){
11920                 return;
11921             }
11922             
11923             f.hide();
11924             
11925         }, this);
11926     },
11927     
11928     showFields : function(items)
11929     {
11930         Roo.each(items, function(i){
11931             
11932             var f = this.findField(i);
11933             
11934             if(!f){
11935                 return;
11936             }
11937             
11938             f.show();
11939             
11940         }, this);
11941     }
11942
11943 });
11944
11945 Roo.apply(Roo.bootstrap.Form, {
11946     
11947     popover : {
11948         
11949         padding : 5,
11950         
11951         isApplied : false,
11952         
11953         isMasked : false,
11954         
11955         form : false,
11956         
11957         target : false,
11958         
11959         toolTip : false,
11960         
11961         intervalID : false,
11962         
11963         maskEl : false,
11964         
11965         apply : function()
11966         {
11967             if(this.isApplied){
11968                 return;
11969             }
11970             
11971             this.maskEl = {
11972                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11973                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11974                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11975                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11976             };
11977             
11978             this.maskEl.top.enableDisplayMode("block");
11979             this.maskEl.left.enableDisplayMode("block");
11980             this.maskEl.bottom.enableDisplayMode("block");
11981             this.maskEl.right.enableDisplayMode("block");
11982             
11983             this.toolTip = new Roo.bootstrap.Tooltip({
11984                 cls : 'roo-form-error-popover',
11985                 alignment : {
11986                     'left' : ['r-l', [-2,0], 'right'],
11987                     'right' : ['l-r', [2,0], 'left'],
11988                     'bottom' : ['tl-bl', [0,2], 'top'],
11989                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11990                 }
11991             });
11992             
11993             this.toolTip.render(Roo.get(document.body));
11994
11995             this.toolTip.el.enableDisplayMode("block");
11996             
11997             Roo.get(document.body).on('click', function(){
11998                 this.unmask();
11999             }, this);
12000             
12001             Roo.get(document.body).on('touchstart', function(){
12002                 this.unmask();
12003             }, this);
12004             
12005             this.isApplied = true
12006         },
12007         
12008         mask : function(form, target)
12009         {
12010             this.form = form;
12011             
12012             this.target = target;
12013             
12014             if(!this.form.errorMask || !target.el){
12015                 return;
12016             }
12017             
12018             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12019             
12020             Roo.log(scrollable);
12021             
12022             var ot = this.target.el.calcOffsetsTo(scrollable);
12023             
12024             var scrollTo = ot[1] - this.form.maskOffset;
12025             
12026             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12027             
12028             scrollable.scrollTo('top', scrollTo);
12029             
12030             var box = this.target.el.getBox();
12031             Roo.log(box);
12032             var zIndex = Roo.bootstrap.Modal.zIndex++;
12033
12034             
12035             this.maskEl.top.setStyle('position', 'absolute');
12036             this.maskEl.top.setStyle('z-index', zIndex);
12037             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12038             this.maskEl.top.setLeft(0);
12039             this.maskEl.top.setTop(0);
12040             this.maskEl.top.show();
12041             
12042             this.maskEl.left.setStyle('position', 'absolute');
12043             this.maskEl.left.setStyle('z-index', zIndex);
12044             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12045             this.maskEl.left.setLeft(0);
12046             this.maskEl.left.setTop(box.y - this.padding);
12047             this.maskEl.left.show();
12048
12049             this.maskEl.bottom.setStyle('position', 'absolute');
12050             this.maskEl.bottom.setStyle('z-index', zIndex);
12051             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12052             this.maskEl.bottom.setLeft(0);
12053             this.maskEl.bottom.setTop(box.bottom + this.padding);
12054             this.maskEl.bottom.show();
12055
12056             this.maskEl.right.setStyle('position', 'absolute');
12057             this.maskEl.right.setStyle('z-index', zIndex);
12058             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12059             this.maskEl.right.setLeft(box.right + this.padding);
12060             this.maskEl.right.setTop(box.y - this.padding);
12061             this.maskEl.right.show();
12062
12063             this.toolTip.bindEl = this.target.el;
12064
12065             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12066
12067             var tip = this.target.blankText;
12068
12069             if(this.target.getValue() !== '' ) {
12070                 
12071                 if (this.target.invalidText.length) {
12072                     tip = this.target.invalidText;
12073                 } else if (this.target.regexText.length){
12074                     tip = this.target.regexText;
12075                 }
12076             }
12077
12078             this.toolTip.show(tip);
12079
12080             this.intervalID = window.setInterval(function() {
12081                 Roo.bootstrap.Form.popover.unmask();
12082             }, 10000);
12083
12084             window.onwheel = function(){ return false;};
12085             
12086             (function(){ this.isMasked = true; }).defer(500, this);
12087             
12088         },
12089         
12090         unmask : function()
12091         {
12092             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12093                 return;
12094             }
12095             
12096             this.maskEl.top.setStyle('position', 'absolute');
12097             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12098             this.maskEl.top.hide();
12099
12100             this.maskEl.left.setStyle('position', 'absolute');
12101             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12102             this.maskEl.left.hide();
12103
12104             this.maskEl.bottom.setStyle('position', 'absolute');
12105             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12106             this.maskEl.bottom.hide();
12107
12108             this.maskEl.right.setStyle('position', 'absolute');
12109             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12110             this.maskEl.right.hide();
12111             
12112             this.toolTip.hide();
12113             
12114             this.toolTip.el.hide();
12115             
12116             window.onwheel = function(){ return true;};
12117             
12118             if(this.intervalID){
12119                 window.clearInterval(this.intervalID);
12120                 this.intervalID = false;
12121             }
12122             
12123             this.isMasked = false;
12124             
12125         }
12126         
12127     }
12128     
12129 });
12130
12131 /*
12132  * Based on:
12133  * Ext JS Library 1.1.1
12134  * Copyright(c) 2006-2007, Ext JS, LLC.
12135  *
12136  * Originally Released Under LGPL - original licence link has changed is not relivant.
12137  *
12138  * Fork - LGPL
12139  * <script type="text/javascript">
12140  */
12141 /**
12142  * @class Roo.form.VTypes
12143  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12144  * @singleton
12145  */
12146 Roo.form.VTypes = function(){
12147     // closure these in so they are only created once.
12148     var alpha = /^[a-zA-Z_]+$/;
12149     var alphanum = /^[a-zA-Z0-9_]+$/;
12150     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12151     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12152
12153     // All these messages and functions are configurable
12154     return {
12155         /**
12156          * The function used to validate email addresses
12157          * @param {String} value The email address
12158          */
12159         'email' : function(v){
12160             return email.test(v);
12161         },
12162         /**
12163          * The error text to display when the email validation function returns false
12164          * @type String
12165          */
12166         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12167         /**
12168          * The keystroke filter mask to be applied on email input
12169          * @type RegExp
12170          */
12171         'emailMask' : /[a-z0-9_\.\-@]/i,
12172
12173         /**
12174          * The function used to validate URLs
12175          * @param {String} value The URL
12176          */
12177         'url' : function(v){
12178             return url.test(v);
12179         },
12180         /**
12181          * The error text to display when the url validation function returns false
12182          * @type String
12183          */
12184         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12185         
12186         /**
12187          * The function used to validate alpha values
12188          * @param {String} value The value
12189          */
12190         'alpha' : function(v){
12191             return alpha.test(v);
12192         },
12193         /**
12194          * The error text to display when the alpha validation function returns false
12195          * @type String
12196          */
12197         'alphaText' : 'This field should only contain letters and _',
12198         /**
12199          * The keystroke filter mask to be applied on alpha input
12200          * @type RegExp
12201          */
12202         'alphaMask' : /[a-z_]/i,
12203
12204         /**
12205          * The function used to validate alphanumeric values
12206          * @param {String} value The value
12207          */
12208         'alphanum' : function(v){
12209             return alphanum.test(v);
12210         },
12211         /**
12212          * The error text to display when the alphanumeric validation function returns false
12213          * @type String
12214          */
12215         'alphanumText' : 'This field should only contain letters, numbers and _',
12216         /**
12217          * The keystroke filter mask to be applied on alphanumeric input
12218          * @type RegExp
12219          */
12220         'alphanumMask' : /[a-z0-9_]/i
12221     };
12222 }();/*
12223  * - LGPL
12224  *
12225  * Input
12226  * 
12227  */
12228
12229 /**
12230  * @class Roo.bootstrap.Input
12231  * @extends Roo.bootstrap.Component
12232  * Bootstrap Input class
12233  * @cfg {Boolean} disabled is it disabled
12234  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12235  * @cfg {String} name name of the input
12236  * @cfg {string} fieldLabel - the label associated
12237  * @cfg {string} placeholder - placeholder to put in text.
12238  * @cfg {string} before - input group add on before
12239  * @cfg {string} after - input group add on after
12240  * @cfg {string} size - (lg|sm) or leave empty..
12241  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12242  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12243  * @cfg {Number} md colspan out of 12 for computer-sized screens
12244  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12245  * @cfg {string} value default value of the input
12246  * @cfg {Number} labelWidth set the width of label 
12247  * @cfg {Number} labellg set the width of label (1-12)
12248  * @cfg {Number} labelmd set the width of label (1-12)
12249  * @cfg {Number} labelsm set the width of label (1-12)
12250  * @cfg {Number} labelxs set the width of label (1-12)
12251  * @cfg {String} labelAlign (top|left)
12252  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12253  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12254  * @cfg {String} indicatorpos (left|right) default left
12255  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12256  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12257  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12258  * @cfg {Roo.bootstrap.Button} before Button to show before
12259  * @cfg {Roo.bootstrap.Button} afterButton to show before
12260  * @cfg {String} align (left|center|right) Default left
12261  * @cfg {Boolean} forceFeedback (true|false) Default false
12262  * 
12263  * @constructor
12264  * Create a new Input
12265  * @param {Object} config The config object
12266  */
12267
12268 Roo.bootstrap.Input = function(config){
12269     
12270     Roo.bootstrap.Input.superclass.constructor.call(this, config);
12271     
12272     this.addEvents({
12273         /**
12274          * @event focus
12275          * Fires when this field receives input focus.
12276          * @param {Roo.form.Field} this
12277          */
12278         focus : true,
12279         /**
12280          * @event blur
12281          * Fires when this field loses input focus.
12282          * @param {Roo.form.Field} this
12283          */
12284         blur : true,
12285         /**
12286          * @event specialkey
12287          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12288          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12289          * @param {Roo.form.Field} this
12290          * @param {Roo.EventObject} e The event object
12291          */
12292         specialkey : true,
12293         /**
12294          * @event change
12295          * Fires just before the field blurs if the field value has changed.
12296          * @param {Roo.form.Field} this
12297          * @param {Mixed} newValue The new value
12298          * @param {Mixed} oldValue The original value
12299          */
12300         change : true,
12301         /**
12302          * @event invalid
12303          * Fires after the field has been marked as invalid.
12304          * @param {Roo.form.Field} this
12305          * @param {String} msg The validation message
12306          */
12307         invalid : true,
12308         /**
12309          * @event valid
12310          * Fires after the field has been validated with no errors.
12311          * @param {Roo.form.Field} this
12312          */
12313         valid : true,
12314          /**
12315          * @event keyup
12316          * Fires after the key up
12317          * @param {Roo.form.Field} this
12318          * @param {Roo.EventObject}  e The event Object
12319          */
12320         keyup : true,
12321         /**
12322          * @event paste
12323          * Fires after the user pastes into input
12324          * @param {Roo.form.Field} this
12325          * @param {Roo.EventObject}  e The event Object
12326          */
12327         paste : true
12328     });
12329 };
12330
12331 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
12332      /**
12333      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12334       automatic validation (defaults to "keyup").
12335      */
12336     validationEvent : "keyup",
12337      /**
12338      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12339      */
12340     validateOnBlur : true,
12341     /**
12342      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12343      */
12344     validationDelay : 250,
12345      /**
12346      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12347      */
12348     focusClass : "x-form-focus",  // not needed???
12349     
12350        
12351     /**
12352      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12353      */
12354     invalidClass : "has-warning",
12355     
12356     /**
12357      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12358      */
12359     validClass : "has-success",
12360     
12361     /**
12362      * @cfg {Boolean} hasFeedback (true|false) default true
12363      */
12364     hasFeedback : true,
12365     
12366     /**
12367      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12368      */
12369     invalidFeedbackClass : "glyphicon-warning-sign",
12370     
12371     /**
12372      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12373      */
12374     validFeedbackClass : "glyphicon-ok",
12375     
12376     /**
12377      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12378      */
12379     selectOnFocus : false,
12380     
12381      /**
12382      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12383      */
12384     maskRe : null,
12385        /**
12386      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12387      */
12388     vtype : null,
12389     
12390       /**
12391      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12392      */
12393     disableKeyFilter : false,
12394     
12395        /**
12396      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12397      */
12398     disabled : false,
12399      /**
12400      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12401      */
12402     allowBlank : true,
12403     /**
12404      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12405      */
12406     blankText : "Please complete this mandatory field",
12407     
12408      /**
12409      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12410      */
12411     minLength : 0,
12412     /**
12413      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12414      */
12415     maxLength : Number.MAX_VALUE,
12416     /**
12417      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12418      */
12419     minLengthText : "The minimum length for this field is {0}",
12420     /**
12421      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12422      */
12423     maxLengthText : "The maximum length for this field is {0}",
12424   
12425     
12426     /**
12427      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12428      * If available, this function will be called only after the basic validators all return true, and will be passed the
12429      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12430      */
12431     validator : null,
12432     /**
12433      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12434      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12435      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12436      */
12437     regex : null,
12438     /**
12439      * @cfg {String} regexText -- Depricated - use Invalid Text
12440      */
12441     regexText : "",
12442     
12443     /**
12444      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12445      */
12446     invalidText : "",
12447     
12448     
12449     
12450     autocomplete: false,
12451     
12452     
12453     fieldLabel : '',
12454     inputType : 'text',
12455     
12456     name : false,
12457     placeholder: false,
12458     before : false,
12459     after : false,
12460     size : false,
12461     hasFocus : false,
12462     preventMark: false,
12463     isFormField : true,
12464     value : '',
12465     labelWidth : 2,
12466     labelAlign : false,
12467     readOnly : false,
12468     align : false,
12469     formatedValue : false,
12470     forceFeedback : false,
12471     
12472     indicatorpos : 'left',
12473     
12474     labellg : 0,
12475     labelmd : 0,
12476     labelsm : 0,
12477     labelxs : 0,
12478     
12479     capture : '',
12480     accept : '',
12481     
12482     parentLabelAlign : function()
12483     {
12484         var parent = this;
12485         while (parent.parent()) {
12486             parent = parent.parent();
12487             if (typeof(parent.labelAlign) !='undefined') {
12488                 return parent.labelAlign;
12489             }
12490         }
12491         return 'left';
12492         
12493     },
12494     
12495     getAutoCreate : function()
12496     {
12497         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12498         
12499         var id = Roo.id();
12500         
12501         var cfg = {};
12502         
12503         if(this.inputType != 'hidden'){
12504             cfg.cls = 'form-group' //input-group
12505         }
12506         
12507         var input =  {
12508             tag: 'input',
12509             id : id,
12510             type : this.inputType,
12511             value : this.value,
12512             cls : 'form-control',
12513             placeholder : this.placeholder || '',
12514             autocomplete : this.autocomplete || 'new-password'
12515         };
12516         if (this.inputType == 'file') {
12517             input.style = 'overflow:hidden'; // why not in CSS?
12518         }
12519         
12520         if(this.capture.length){
12521             input.capture = this.capture;
12522         }
12523         
12524         if(this.accept.length){
12525             input.accept = this.accept + "/*";
12526         }
12527         
12528         if(this.align){
12529             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12530         }
12531         
12532         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12533             input.maxLength = this.maxLength;
12534         }
12535         
12536         if (this.disabled) {
12537             input.disabled=true;
12538         }
12539         
12540         if (this.readOnly) {
12541             input.readonly=true;
12542         }
12543         
12544         if (this.name) {
12545             input.name = this.name;
12546         }
12547         
12548         if (this.size) {
12549             input.cls += ' input-' + this.size;
12550         }
12551         
12552         var settings=this;
12553         ['xs','sm','md','lg'].map(function(size){
12554             if (settings[size]) {
12555                 cfg.cls += ' col-' + size + '-' + settings[size];
12556             }
12557         });
12558         
12559         var inputblock = input;
12560         
12561         var feedback = {
12562             tag: 'span',
12563             cls: 'glyphicon form-control-feedback'
12564         };
12565             
12566         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12567             
12568             inputblock = {
12569                 cls : 'has-feedback',
12570                 cn :  [
12571                     input,
12572                     feedback
12573                 ] 
12574             };  
12575         }
12576         
12577         if (this.before || this.after) {
12578             
12579             inputblock = {
12580                 cls : 'input-group',
12581                 cn :  [] 
12582             };
12583             
12584             if (this.before && typeof(this.before) == 'string') {
12585                 
12586                 inputblock.cn.push({
12587                     tag :'span',
12588                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12589                     html : this.before
12590                 });
12591             }
12592             if (this.before && typeof(this.before) == 'object') {
12593                 this.before = Roo.factory(this.before);
12594                 
12595                 inputblock.cn.push({
12596                     tag :'span',
12597                     cls : 'roo-input-before input-group-prepend   input-group-' +
12598                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12599                 });
12600             }
12601             
12602             inputblock.cn.push(input);
12603             
12604             if (this.after && typeof(this.after) == 'string') {
12605                 inputblock.cn.push({
12606                     tag :'span',
12607                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12608                     html : this.after
12609                 });
12610             }
12611             if (this.after && typeof(this.after) == 'object') {
12612                 this.after = Roo.factory(this.after);
12613                 
12614                 inputblock.cn.push({
12615                     tag :'span',
12616                     cls : 'roo-input-after input-group-append  input-group-' +
12617                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12618                 });
12619             }
12620             
12621             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12622                 inputblock.cls += ' has-feedback';
12623                 inputblock.cn.push(feedback);
12624             }
12625         };
12626         var indicator = {
12627             tag : 'i',
12628             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12629             tooltip : 'This field is required'
12630         };
12631         if (this.allowBlank ) {
12632             indicator.style = this.allowBlank ? ' display:none' : '';
12633         }
12634         if (align ==='left' && this.fieldLabel.length) {
12635             
12636             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12637             
12638             cfg.cn = [
12639                 indicator,
12640                 {
12641                     tag: 'label',
12642                     'for' :  id,
12643                     cls : 'control-label col-form-label',
12644                     html : this.fieldLabel
12645
12646                 },
12647                 {
12648                     cls : "", 
12649                     cn: [
12650                         inputblock
12651                     ]
12652                 }
12653             ];
12654             
12655             var labelCfg = cfg.cn[1];
12656             var contentCfg = cfg.cn[2];
12657             
12658             if(this.indicatorpos == 'right'){
12659                 cfg.cn = [
12660                     {
12661                         tag: 'label',
12662                         'for' :  id,
12663                         cls : 'control-label col-form-label',
12664                         cn : [
12665                             {
12666                                 tag : 'span',
12667                                 html : this.fieldLabel
12668                             },
12669                             indicator
12670                         ]
12671                     },
12672                     {
12673                         cls : "",
12674                         cn: [
12675                             inputblock
12676                         ]
12677                     }
12678
12679                 ];
12680                 
12681                 labelCfg = cfg.cn[0];
12682                 contentCfg = cfg.cn[1];
12683             
12684             }
12685             
12686             if(this.labelWidth > 12){
12687                 labelCfg.style = "width: " + this.labelWidth + 'px';
12688             }
12689             
12690             if(this.labelWidth < 13 && this.labelmd == 0){
12691                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12692             }
12693             
12694             if(this.labellg > 0){
12695                 labelCfg.cls += ' col-lg-' + this.labellg;
12696                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12697             }
12698             
12699             if(this.labelmd > 0){
12700                 labelCfg.cls += ' col-md-' + this.labelmd;
12701                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12702             }
12703             
12704             if(this.labelsm > 0){
12705                 labelCfg.cls += ' col-sm-' + this.labelsm;
12706                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12707             }
12708             
12709             if(this.labelxs > 0){
12710                 labelCfg.cls += ' col-xs-' + this.labelxs;
12711                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12712             }
12713             
12714             
12715         } else if ( this.fieldLabel.length) {
12716                 
12717             
12718             
12719             cfg.cn = [
12720                 {
12721                     tag : 'i',
12722                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12723                     tooltip : 'This field is required',
12724                     style : this.allowBlank ? ' display:none' : '' 
12725                 },
12726                 {
12727                     tag: 'label',
12728                    //cls : 'input-group-addon',
12729                     html : this.fieldLabel
12730
12731                 },
12732
12733                inputblock
12734
12735            ];
12736            
12737            if(this.indicatorpos == 'right'){
12738        
12739                 cfg.cn = [
12740                     {
12741                         tag: 'label',
12742                        //cls : 'input-group-addon',
12743                         html : this.fieldLabel
12744
12745                     },
12746                     {
12747                         tag : 'i',
12748                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12749                         tooltip : 'This field is required',
12750                         style : this.allowBlank ? ' display:none' : '' 
12751                     },
12752
12753                    inputblock
12754
12755                ];
12756
12757             }
12758
12759         } else {
12760             
12761             cfg.cn = [
12762
12763                     inputblock
12764
12765             ];
12766                 
12767                 
12768         };
12769         
12770         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12771            cfg.cls += ' navbar-form';
12772         }
12773         
12774         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12775             // on BS4 we do this only if not form 
12776             cfg.cls += ' navbar-form';
12777             cfg.tag = 'li';
12778         }
12779         
12780         return cfg;
12781         
12782     },
12783     /**
12784      * return the real input element.
12785      */
12786     inputEl: function ()
12787     {
12788         return this.el.select('input.form-control',true).first();
12789     },
12790     
12791     tooltipEl : function()
12792     {
12793         return this.inputEl();
12794     },
12795     
12796     indicatorEl : function()
12797     {
12798         if (Roo.bootstrap.version == 4) {
12799             return false; // not enabled in v4 yet.
12800         }
12801         
12802         var indicator = this.el.select('i.roo-required-indicator',true).first();
12803         
12804         if(!indicator){
12805             return false;
12806         }
12807         
12808         return indicator;
12809         
12810     },
12811     
12812     setDisabled : function(v)
12813     {
12814         var i  = this.inputEl().dom;
12815         if (!v) {
12816             i.removeAttribute('disabled');
12817             return;
12818             
12819         }
12820         i.setAttribute('disabled','true');
12821     },
12822     initEvents : function()
12823     {
12824           
12825         this.inputEl().on("keydown" , this.fireKey,  this);
12826         this.inputEl().on("focus", this.onFocus,  this);
12827         this.inputEl().on("blur", this.onBlur,  this);
12828         
12829         this.inputEl().relayEvent('keyup', this);
12830         this.inputEl().relayEvent('paste', this);
12831         
12832         this.indicator = this.indicatorEl();
12833         
12834         if(this.indicator){
12835             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12836         }
12837  
12838         // reference to original value for reset
12839         this.originalValue = this.getValue();
12840         //Roo.form.TextField.superclass.initEvents.call(this);
12841         if(this.validationEvent == 'keyup'){
12842             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12843             this.inputEl().on('keyup', this.filterValidation, this);
12844         }
12845         else if(this.validationEvent !== false){
12846             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12847         }
12848         
12849         if(this.selectOnFocus){
12850             this.on("focus", this.preFocus, this);
12851             
12852         }
12853         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12854             this.inputEl().on("keypress", this.filterKeys, this);
12855         } else {
12856             this.inputEl().relayEvent('keypress', this);
12857         }
12858        /* if(this.grow){
12859             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12860             this.el.on("click", this.autoSize,  this);
12861         }
12862         */
12863         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12864             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12865         }
12866         
12867         if (typeof(this.before) == 'object') {
12868             this.before.render(this.el.select('.roo-input-before',true).first());
12869         }
12870         if (typeof(this.after) == 'object') {
12871             this.after.render(this.el.select('.roo-input-after',true).first());
12872         }
12873         
12874         this.inputEl().on('change', this.onChange, this);
12875         
12876     },
12877     filterValidation : function(e){
12878         if(!e.isNavKeyPress()){
12879             this.validationTask.delay(this.validationDelay);
12880         }
12881     },
12882      /**
12883      * Validates the field value
12884      * @return {Boolean} True if the value is valid, else false
12885      */
12886     validate : function(){
12887         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12888         if(this.disabled || this.validateValue(this.getRawValue())){
12889             this.markValid();
12890             return true;
12891         }
12892         
12893         this.markInvalid();
12894         return false;
12895     },
12896     
12897     
12898     /**
12899      * Validates a value according to the field's validation rules and marks the field as invalid
12900      * if the validation fails
12901      * @param {Mixed} value The value to validate
12902      * @return {Boolean} True if the value is valid, else false
12903      */
12904     validateValue : function(value)
12905     {
12906         if(this.getVisibilityEl().hasClass('hidden')){
12907             return true;
12908         }
12909         
12910         if(value.length < 1)  { // if it's blank
12911             if(this.allowBlank){
12912                 return true;
12913             }
12914             return false;
12915         }
12916         
12917         if(value.length < this.minLength){
12918             return false;
12919         }
12920         if(value.length > this.maxLength){
12921             return false;
12922         }
12923         if(this.vtype){
12924             var vt = Roo.form.VTypes;
12925             if(!vt[this.vtype](value, this)){
12926                 return false;
12927             }
12928         }
12929         if(typeof this.validator == "function"){
12930             var msg = this.validator(value);
12931             if(msg !== true){
12932                 return false;
12933             }
12934             if (typeof(msg) == 'string') {
12935                 this.invalidText = msg;
12936             }
12937         }
12938         
12939         if(this.regex && !this.regex.test(value)){
12940             return false;
12941         }
12942         
12943         return true;
12944     },
12945     
12946      // private
12947     fireKey : function(e){
12948         //Roo.log('field ' + e.getKey());
12949         if(e.isNavKeyPress()){
12950             this.fireEvent("specialkey", this, e);
12951         }
12952     },
12953     focus : function (selectText){
12954         if(this.rendered){
12955             this.inputEl().focus();
12956             if(selectText === true){
12957                 this.inputEl().dom.select();
12958             }
12959         }
12960         return this;
12961     } ,
12962     
12963     onFocus : function(){
12964         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12965            // this.el.addClass(this.focusClass);
12966         }
12967         if(!this.hasFocus){
12968             this.hasFocus = true;
12969             this.startValue = this.getValue();
12970             this.fireEvent("focus", this);
12971         }
12972     },
12973     
12974     beforeBlur : Roo.emptyFn,
12975
12976     
12977     // private
12978     onBlur : function(){
12979         this.beforeBlur();
12980         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12981             //this.el.removeClass(this.focusClass);
12982         }
12983         this.hasFocus = false;
12984         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12985             this.validate();
12986         }
12987         var v = this.getValue();
12988         if(String(v) !== String(this.startValue)){
12989             this.fireEvent('change', this, v, this.startValue);
12990         }
12991         this.fireEvent("blur", this);
12992     },
12993     
12994     onChange : function(e)
12995     {
12996         var v = this.getValue();
12997         if(String(v) !== String(this.startValue)){
12998             this.fireEvent('change', this, v, this.startValue);
12999         }
13000         
13001     },
13002     
13003     /**
13004      * Resets the current field value to the originally loaded value and clears any validation messages
13005      */
13006     reset : function(){
13007         this.setValue(this.originalValue);
13008         this.validate();
13009     },
13010      /**
13011      * Returns the name of the field
13012      * @return {Mixed} name The name field
13013      */
13014     getName: function(){
13015         return this.name;
13016     },
13017      /**
13018      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13019      * @return {Mixed} value The field value
13020      */
13021     getValue : function(){
13022         
13023         var v = this.inputEl().getValue();
13024         
13025         return v;
13026     },
13027     /**
13028      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13029      * @return {Mixed} value The field value
13030      */
13031     getRawValue : function(){
13032         var v = this.inputEl().getValue();
13033         
13034         return v;
13035     },
13036     
13037     /**
13038      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13039      * @param {Mixed} value The value to set
13040      */
13041     setRawValue : function(v){
13042         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13043     },
13044     
13045     selectText : function(start, end){
13046         var v = this.getRawValue();
13047         if(v.length > 0){
13048             start = start === undefined ? 0 : start;
13049             end = end === undefined ? v.length : end;
13050             var d = this.inputEl().dom;
13051             if(d.setSelectionRange){
13052                 d.setSelectionRange(start, end);
13053             }else if(d.createTextRange){
13054                 var range = d.createTextRange();
13055                 range.moveStart("character", start);
13056                 range.moveEnd("character", v.length-end);
13057                 range.select();
13058             }
13059         }
13060     },
13061     
13062     /**
13063      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13064      * @param {Mixed} value The value to set
13065      */
13066     setValue : function(v){
13067         this.value = v;
13068         if(this.rendered){
13069             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13070             this.validate();
13071         }
13072     },
13073     
13074     /*
13075     processValue : function(value){
13076         if(this.stripCharsRe){
13077             var newValue = value.replace(this.stripCharsRe, '');
13078             if(newValue !== value){
13079                 this.setRawValue(newValue);
13080                 return newValue;
13081             }
13082         }
13083         return value;
13084     },
13085   */
13086     preFocus : function(){
13087         
13088         if(this.selectOnFocus){
13089             this.inputEl().dom.select();
13090         }
13091     },
13092     filterKeys : function(e){
13093         var k = e.getKey();
13094         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13095             return;
13096         }
13097         var c = e.getCharCode(), cc = String.fromCharCode(c);
13098         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13099             return;
13100         }
13101         if(!this.maskRe.test(cc)){
13102             e.stopEvent();
13103         }
13104     },
13105      /**
13106      * Clear any invalid styles/messages for this field
13107      */
13108     clearInvalid : function(){
13109         
13110         if(!this.el || this.preventMark){ // not rendered
13111             return;
13112         }
13113         
13114         
13115         this.el.removeClass([this.invalidClass, 'is-invalid']);
13116         
13117         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13118             
13119             var feedback = this.el.select('.form-control-feedback', true).first();
13120             
13121             if(feedback){
13122                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13123             }
13124             
13125         }
13126         
13127         if(this.indicator){
13128             this.indicator.removeClass('visible');
13129             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13130         }
13131         
13132         this.fireEvent('valid', this);
13133     },
13134     
13135      /**
13136      * Mark this field as valid
13137      */
13138     markValid : function()
13139     {
13140         if(!this.el  || this.preventMark){ // not rendered...
13141             return;
13142         }
13143         
13144         this.el.removeClass([this.invalidClass, this.validClass]);
13145         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13146
13147         var feedback = this.el.select('.form-control-feedback', true).first();
13148             
13149         if(feedback){
13150             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13151         }
13152         
13153         if(this.indicator){
13154             this.indicator.removeClass('visible');
13155             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13156         }
13157         
13158         if(this.disabled){
13159             return;
13160         }
13161         
13162            
13163         if(this.allowBlank && !this.getRawValue().length){
13164             return;
13165         }
13166         if (Roo.bootstrap.version == 3) {
13167             this.el.addClass(this.validClass);
13168         } else {
13169             this.inputEl().addClass('is-valid');
13170         }
13171
13172         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13173             
13174             var feedback = this.el.select('.form-control-feedback', true).first();
13175             
13176             if(feedback){
13177                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13178                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13179             }
13180             
13181         }
13182         
13183         this.fireEvent('valid', this);
13184     },
13185     
13186      /**
13187      * Mark this field as invalid
13188      * @param {String} msg The validation message
13189      */
13190     markInvalid : function(msg)
13191     {
13192         if(!this.el  || this.preventMark){ // not rendered
13193             return;
13194         }
13195         
13196         this.el.removeClass([this.invalidClass, this.validClass]);
13197         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13198         
13199         var feedback = this.el.select('.form-control-feedback', true).first();
13200             
13201         if(feedback){
13202             this.el.select('.form-control-feedback', true).first().removeClass(
13203                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13204         }
13205
13206         if(this.disabled){
13207             return;
13208         }
13209         
13210         if(this.allowBlank && !this.getRawValue().length){
13211             return;
13212         }
13213         
13214         if(this.indicator){
13215             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13216             this.indicator.addClass('visible');
13217         }
13218         if (Roo.bootstrap.version == 3) {
13219             this.el.addClass(this.invalidClass);
13220         } else {
13221             this.inputEl().addClass('is-invalid');
13222         }
13223         
13224         
13225         
13226         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13227             
13228             var feedback = this.el.select('.form-control-feedback', true).first();
13229             
13230             if(feedback){
13231                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13232                 
13233                 if(this.getValue().length || this.forceFeedback){
13234                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13235                 }
13236                 
13237             }
13238             
13239         }
13240         
13241         this.fireEvent('invalid', this, msg);
13242     },
13243     // private
13244     SafariOnKeyDown : function(event)
13245     {
13246         // this is a workaround for a password hang bug on chrome/ webkit.
13247         if (this.inputEl().dom.type != 'password') {
13248             return;
13249         }
13250         
13251         var isSelectAll = false;
13252         
13253         if(this.inputEl().dom.selectionEnd > 0){
13254             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13255         }
13256         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13257             event.preventDefault();
13258             this.setValue('');
13259             return;
13260         }
13261         
13262         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13263             
13264             event.preventDefault();
13265             // this is very hacky as keydown always get's upper case.
13266             //
13267             var cc = String.fromCharCode(event.getCharCode());
13268             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13269             
13270         }
13271     },
13272     adjustWidth : function(tag, w){
13273         tag = tag.toLowerCase();
13274         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13275             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13276                 if(tag == 'input'){
13277                     return w + 2;
13278                 }
13279                 if(tag == 'textarea'){
13280                     return w-2;
13281                 }
13282             }else if(Roo.isOpera){
13283                 if(tag == 'input'){
13284                     return w + 2;
13285                 }
13286                 if(tag == 'textarea'){
13287                     return w-2;
13288                 }
13289             }
13290         }
13291         return w;
13292     },
13293     
13294     setFieldLabel : function(v)
13295     {
13296         if(!this.rendered){
13297             return;
13298         }
13299         
13300         if(this.indicatorEl()){
13301             var ar = this.el.select('label > span',true);
13302             
13303             if (ar.elements.length) {
13304                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13305                 this.fieldLabel = v;
13306                 return;
13307             }
13308             
13309             var br = this.el.select('label',true);
13310             
13311             if(br.elements.length) {
13312                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13313                 this.fieldLabel = v;
13314                 return;
13315             }
13316             
13317             Roo.log('Cannot Found any of label > span || label in input');
13318             return;
13319         }
13320         
13321         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13322         this.fieldLabel = v;
13323         
13324         
13325     }
13326 });
13327
13328  
13329 /*
13330  * - LGPL
13331  *
13332  * Input
13333  * 
13334  */
13335
13336 /**
13337  * @class Roo.bootstrap.TextArea
13338  * @extends Roo.bootstrap.Input
13339  * Bootstrap TextArea class
13340  * @cfg {Number} cols Specifies the visible width of a text area
13341  * @cfg {Number} rows Specifies the visible number of lines in a text area
13342  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13343  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13344  * @cfg {string} html text
13345  * 
13346  * @constructor
13347  * Create a new TextArea
13348  * @param {Object} config The config object
13349  */
13350
13351 Roo.bootstrap.TextArea = function(config){
13352     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
13353    
13354 };
13355
13356 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
13357      
13358     cols : false,
13359     rows : 5,
13360     readOnly : false,
13361     warp : 'soft',
13362     resize : false,
13363     value: false,
13364     html: false,
13365     
13366     getAutoCreate : function(){
13367         
13368         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13369         
13370         var id = Roo.id();
13371         
13372         var cfg = {};
13373         
13374         if(this.inputType != 'hidden'){
13375             cfg.cls = 'form-group' //input-group
13376         }
13377         
13378         var input =  {
13379             tag: 'textarea',
13380             id : id,
13381             warp : this.warp,
13382             rows : this.rows,
13383             value : this.value || '',
13384             html: this.html || '',
13385             cls : 'form-control',
13386             placeholder : this.placeholder || '' 
13387             
13388         };
13389         
13390         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13391             input.maxLength = this.maxLength;
13392         }
13393         
13394         if(this.resize){
13395             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13396         }
13397         
13398         if(this.cols){
13399             input.cols = this.cols;
13400         }
13401         
13402         if (this.readOnly) {
13403             input.readonly = true;
13404         }
13405         
13406         if (this.name) {
13407             input.name = this.name;
13408         }
13409         
13410         if (this.size) {
13411             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13412         }
13413         
13414         var settings=this;
13415         ['xs','sm','md','lg'].map(function(size){
13416             if (settings[size]) {
13417                 cfg.cls += ' col-' + size + '-' + settings[size];
13418             }
13419         });
13420         
13421         var inputblock = input;
13422         
13423         if(this.hasFeedback && !this.allowBlank){
13424             
13425             var feedback = {
13426                 tag: 'span',
13427                 cls: 'glyphicon form-control-feedback'
13428             };
13429
13430             inputblock = {
13431                 cls : 'has-feedback',
13432                 cn :  [
13433                     input,
13434                     feedback
13435                 ] 
13436             };  
13437         }
13438         
13439         
13440         if (this.before || this.after) {
13441             
13442             inputblock = {
13443                 cls : 'input-group',
13444                 cn :  [] 
13445             };
13446             if (this.before) {
13447                 inputblock.cn.push({
13448                     tag :'span',
13449                     cls : 'input-group-addon',
13450                     html : this.before
13451                 });
13452             }
13453             
13454             inputblock.cn.push(input);
13455             
13456             if(this.hasFeedback && !this.allowBlank){
13457                 inputblock.cls += ' has-feedback';
13458                 inputblock.cn.push(feedback);
13459             }
13460             
13461             if (this.after) {
13462                 inputblock.cn.push({
13463                     tag :'span',
13464                     cls : 'input-group-addon',
13465                     html : this.after
13466                 });
13467             }
13468             
13469         }
13470         
13471         if (align ==='left' && this.fieldLabel.length) {
13472             cfg.cn = [
13473                 {
13474                     tag: 'label',
13475                     'for' :  id,
13476                     cls : 'control-label',
13477                     html : this.fieldLabel
13478                 },
13479                 {
13480                     cls : "",
13481                     cn: [
13482                         inputblock
13483                     ]
13484                 }
13485
13486             ];
13487             
13488             if(this.labelWidth > 12){
13489                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13490             }
13491
13492             if(this.labelWidth < 13 && this.labelmd == 0){
13493                 this.labelmd = this.labelWidth;
13494             }
13495
13496             if(this.labellg > 0){
13497                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13498                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13499             }
13500
13501             if(this.labelmd > 0){
13502                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13503                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13504             }
13505
13506             if(this.labelsm > 0){
13507                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13508                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13509             }
13510
13511             if(this.labelxs > 0){
13512                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13513                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13514             }
13515             
13516         } else if ( this.fieldLabel.length) {
13517             cfg.cn = [
13518
13519                {
13520                    tag: 'label',
13521                    //cls : 'input-group-addon',
13522                    html : this.fieldLabel
13523
13524                },
13525
13526                inputblock
13527
13528            ];
13529
13530         } else {
13531
13532             cfg.cn = [
13533
13534                 inputblock
13535
13536             ];
13537                 
13538         }
13539         
13540         if (this.disabled) {
13541             input.disabled=true;
13542         }
13543         
13544         return cfg;
13545         
13546     },
13547     /**
13548      * return the real textarea element.
13549      */
13550     inputEl: function ()
13551     {
13552         return this.el.select('textarea.form-control',true).first();
13553     },
13554     
13555     /**
13556      * Clear any invalid styles/messages for this field
13557      */
13558     clearInvalid : function()
13559     {
13560         
13561         if(!this.el || this.preventMark){ // not rendered
13562             return;
13563         }
13564         
13565         var label = this.el.select('label', true).first();
13566         var icon = this.el.select('i.fa-star', true).first();
13567         
13568         if(label && icon){
13569             icon.remove();
13570         }
13571         this.el.removeClass( this.validClass);
13572         this.inputEl().removeClass('is-invalid');
13573          
13574         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13575             
13576             var feedback = this.el.select('.form-control-feedback', true).first();
13577             
13578             if(feedback){
13579                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13580             }
13581             
13582         }
13583         
13584         this.fireEvent('valid', this);
13585     },
13586     
13587      /**
13588      * Mark this field as valid
13589      */
13590     markValid : function()
13591     {
13592         if(!this.el  || this.preventMark){ // not rendered
13593             return;
13594         }
13595         
13596         this.el.removeClass([this.invalidClass, this.validClass]);
13597         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13598         
13599         var feedback = this.el.select('.form-control-feedback', true).first();
13600             
13601         if(feedback){
13602             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13603         }
13604
13605         if(this.disabled || this.allowBlank){
13606             return;
13607         }
13608         
13609         var label = this.el.select('label', true).first();
13610         var icon = this.el.select('i.fa-star', true).first();
13611         
13612         if(label && icon){
13613             icon.remove();
13614         }
13615         if (Roo.bootstrap.version == 3) {
13616             this.el.addClass(this.validClass);
13617         } else {
13618             this.inputEl().addClass('is-valid');
13619         }
13620         
13621         
13622         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13623             
13624             var feedback = this.el.select('.form-control-feedback', true).first();
13625             
13626             if(feedback){
13627                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13628                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13629             }
13630             
13631         }
13632         
13633         this.fireEvent('valid', this);
13634     },
13635     
13636      /**
13637      * Mark this field as invalid
13638      * @param {String} msg The validation message
13639      */
13640     markInvalid : function(msg)
13641     {
13642         if(!this.el  || this.preventMark){ // not rendered
13643             return;
13644         }
13645         
13646         this.el.removeClass([this.invalidClass, this.validClass]);
13647         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13648         
13649         var feedback = this.el.select('.form-control-feedback', true).first();
13650             
13651         if(feedback){
13652             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13653         }
13654
13655         if(this.disabled || this.allowBlank){
13656             return;
13657         }
13658         
13659         var label = this.el.select('label', true).first();
13660         var icon = this.el.select('i.fa-star', true).first();
13661         
13662         if(!this.getValue().length && label && !icon){
13663             this.el.createChild({
13664                 tag : 'i',
13665                 cls : 'text-danger fa fa-lg fa-star',
13666                 tooltip : 'This field is required',
13667                 style : 'margin-right:5px;'
13668             }, label, true);
13669         }
13670         
13671         if (Roo.bootstrap.version == 3) {
13672             this.el.addClass(this.invalidClass);
13673         } else {
13674             this.inputEl().addClass('is-invalid');
13675         }
13676         
13677         // fixme ... this may be depricated need to test..
13678         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13679             
13680             var feedback = this.el.select('.form-control-feedback', true).first();
13681             
13682             if(feedback){
13683                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13684                 
13685                 if(this.getValue().length || this.forceFeedback){
13686                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13687                 }
13688                 
13689             }
13690             
13691         }
13692         
13693         this.fireEvent('invalid', this, msg);
13694     }
13695 });
13696
13697  
13698 /*
13699  * - LGPL
13700  *
13701  * trigger field - base class for combo..
13702  * 
13703  */
13704  
13705 /**
13706  * @class Roo.bootstrap.TriggerField
13707  * @extends Roo.bootstrap.Input
13708  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13709  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13710  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13711  * for which you can provide a custom implementation.  For example:
13712  * <pre><code>
13713 var trigger = new Roo.bootstrap.TriggerField();
13714 trigger.onTriggerClick = myTriggerFn;
13715 trigger.applyTo('my-field');
13716 </code></pre>
13717  *
13718  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13719  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13720  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13721  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13722  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13723
13724  * @constructor
13725  * Create a new TriggerField.
13726  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13727  * to the base TextField)
13728  */
13729 Roo.bootstrap.TriggerField = function(config){
13730     this.mimicing = false;
13731     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13732 };
13733
13734 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13735     /**
13736      * @cfg {String} triggerClass A CSS class to apply to the trigger
13737      */
13738      /**
13739      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13740      */
13741     hideTrigger:false,
13742
13743     /**
13744      * @cfg {Boolean} removable (true|false) special filter default false
13745      */
13746     removable : false,
13747     
13748     /** @cfg {Boolean} grow @hide */
13749     /** @cfg {Number} growMin @hide */
13750     /** @cfg {Number} growMax @hide */
13751
13752     /**
13753      * @hide 
13754      * @method
13755      */
13756     autoSize: Roo.emptyFn,
13757     // private
13758     monitorTab : true,
13759     // private
13760     deferHeight : true,
13761
13762     
13763     actionMode : 'wrap',
13764     
13765     caret : false,
13766     
13767     
13768     getAutoCreate : function(){
13769        
13770         var align = this.labelAlign || this.parentLabelAlign();
13771         
13772         var id = Roo.id();
13773         
13774         var cfg = {
13775             cls: 'form-group' //input-group
13776         };
13777         
13778         
13779         var input =  {
13780             tag: 'input',
13781             id : id,
13782             type : this.inputType,
13783             cls : 'form-control',
13784             autocomplete: 'new-password',
13785             placeholder : this.placeholder || '' 
13786             
13787         };
13788         if (this.name) {
13789             input.name = this.name;
13790         }
13791         if (this.size) {
13792             input.cls += ' input-' + this.size;
13793         }
13794         
13795         if (this.disabled) {
13796             input.disabled=true;
13797         }
13798         
13799         var inputblock = input;
13800         
13801         if(this.hasFeedback && !this.allowBlank){
13802             
13803             var feedback = {
13804                 tag: 'span',
13805                 cls: 'glyphicon form-control-feedback'
13806             };
13807             
13808             if(this.removable && !this.editable  ){
13809                 inputblock = {
13810                     cls : 'has-feedback',
13811                     cn :  [
13812                         inputblock,
13813                         {
13814                             tag: 'button',
13815                             html : 'x',
13816                             cls : 'roo-combo-removable-btn close'
13817                         },
13818                         feedback
13819                     ] 
13820                 };
13821             } else {
13822                 inputblock = {
13823                     cls : 'has-feedback',
13824                     cn :  [
13825                         inputblock,
13826                         feedback
13827                     ] 
13828                 };
13829             }
13830
13831         } else {
13832             if(this.removable && !this.editable ){
13833                 inputblock = {
13834                     cls : 'roo-removable',
13835                     cn :  [
13836                         inputblock,
13837                         {
13838                             tag: 'button',
13839                             html : 'x',
13840                             cls : 'roo-combo-removable-btn close'
13841                         }
13842                     ] 
13843                 };
13844             }
13845         }
13846         
13847         if (this.before || this.after) {
13848             
13849             inputblock = {
13850                 cls : 'input-group',
13851                 cn :  [] 
13852             };
13853             if (this.before) {
13854                 inputblock.cn.push({
13855                     tag :'span',
13856                     cls : 'input-group-addon input-group-prepend input-group-text',
13857                     html : this.before
13858                 });
13859             }
13860             
13861             inputblock.cn.push(input);
13862             
13863             if(this.hasFeedback && !this.allowBlank){
13864                 inputblock.cls += ' has-feedback';
13865                 inputblock.cn.push(feedback);
13866             }
13867             
13868             if (this.after) {
13869                 inputblock.cn.push({
13870                     tag :'span',
13871                     cls : 'input-group-addon input-group-append input-group-text',
13872                     html : this.after
13873                 });
13874             }
13875             
13876         };
13877         
13878       
13879         
13880         var ibwrap = inputblock;
13881         
13882         if(this.multiple){
13883             ibwrap = {
13884                 tag: 'ul',
13885                 cls: 'roo-select2-choices',
13886                 cn:[
13887                     {
13888                         tag: 'li',
13889                         cls: 'roo-select2-search-field',
13890                         cn: [
13891
13892                             inputblock
13893                         ]
13894                     }
13895                 ]
13896             };
13897                 
13898         }
13899         
13900         var combobox = {
13901             cls: 'roo-select2-container input-group',
13902             cn: [
13903                  {
13904                     tag: 'input',
13905                     type : 'hidden',
13906                     cls: 'form-hidden-field'
13907                 },
13908                 ibwrap
13909             ]
13910         };
13911         
13912         if(!this.multiple && this.showToggleBtn){
13913             
13914             var caret = {
13915                         tag: 'span',
13916                         cls: 'caret'
13917              };
13918             if (this.caret != false) {
13919                 caret = {
13920                      tag: 'i',
13921                      cls: 'fa fa-' + this.caret
13922                 };
13923                 
13924             }
13925             
13926             combobox.cn.push({
13927                 tag :'span',
13928                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13929                 cn : [
13930                     Roo.bootstrap.version == 3 ? caret : '',
13931                     {
13932                         tag: 'span',
13933                         cls: 'combobox-clear',
13934                         cn  : [
13935                             {
13936                                 tag : 'i',
13937                                 cls: 'icon-remove'
13938                             }
13939                         ]
13940                     }
13941                 ]
13942
13943             })
13944         }
13945         
13946         if(this.multiple){
13947             combobox.cls += ' roo-select2-container-multi';
13948         }
13949          var indicator = {
13950             tag : 'i',
13951             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13952             tooltip : 'This field is required'
13953         };
13954         if (Roo.bootstrap.version == 4) {
13955             indicator = {
13956                 tag : 'i',
13957                 style : 'display:none'
13958             };
13959         }
13960         
13961         
13962         if (align ==='left' && this.fieldLabel.length) {
13963             
13964             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13965
13966             cfg.cn = [
13967                 indicator,
13968                 {
13969                     tag: 'label',
13970                     'for' :  id,
13971                     cls : 'control-label',
13972                     html : this.fieldLabel
13973
13974                 },
13975                 {
13976                     cls : "", 
13977                     cn: [
13978                         combobox
13979                     ]
13980                 }
13981
13982             ];
13983             
13984             var labelCfg = cfg.cn[1];
13985             var contentCfg = cfg.cn[2];
13986             
13987             if(this.indicatorpos == 'right'){
13988                 cfg.cn = [
13989                     {
13990                         tag: 'label',
13991                         'for' :  id,
13992                         cls : 'control-label',
13993                         cn : [
13994                             {
13995                                 tag : 'span',
13996                                 html : this.fieldLabel
13997                             },
13998                             indicator
13999                         ]
14000                     },
14001                     {
14002                         cls : "", 
14003                         cn: [
14004                             combobox
14005                         ]
14006                     }
14007
14008                 ];
14009                 
14010                 labelCfg = cfg.cn[0];
14011                 contentCfg = cfg.cn[1];
14012             }
14013             
14014             if(this.labelWidth > 12){
14015                 labelCfg.style = "width: " + this.labelWidth + 'px';
14016             }
14017             
14018             if(this.labelWidth < 13 && this.labelmd == 0){
14019                 this.labelmd = this.labelWidth;
14020             }
14021             
14022             if(this.labellg > 0){
14023                 labelCfg.cls += ' col-lg-' + this.labellg;
14024                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14025             }
14026             
14027             if(this.labelmd > 0){
14028                 labelCfg.cls += ' col-md-' + this.labelmd;
14029                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14030             }
14031             
14032             if(this.labelsm > 0){
14033                 labelCfg.cls += ' col-sm-' + this.labelsm;
14034                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14035             }
14036             
14037             if(this.labelxs > 0){
14038                 labelCfg.cls += ' col-xs-' + this.labelxs;
14039                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14040             }
14041             
14042         } else if ( this.fieldLabel.length) {
14043 //                Roo.log(" label");
14044             cfg.cn = [
14045                 indicator,
14046                {
14047                    tag: 'label',
14048                    //cls : 'input-group-addon',
14049                    html : this.fieldLabel
14050
14051                },
14052
14053                combobox
14054
14055             ];
14056             
14057             if(this.indicatorpos == 'right'){
14058                 
14059                 cfg.cn = [
14060                     {
14061                        tag: 'label',
14062                        cn : [
14063                            {
14064                                tag : 'span',
14065                                html : this.fieldLabel
14066                            },
14067                            indicator
14068                        ]
14069
14070                     },
14071                     combobox
14072
14073                 ];
14074
14075             }
14076
14077         } else {
14078             
14079 //                Roo.log(" no label && no align");
14080                 cfg = combobox
14081                      
14082                 
14083         }
14084         
14085         var settings=this;
14086         ['xs','sm','md','lg'].map(function(size){
14087             if (settings[size]) {
14088                 cfg.cls += ' col-' + size + '-' + settings[size];
14089             }
14090         });
14091         
14092         return cfg;
14093         
14094     },
14095     
14096     
14097     
14098     // private
14099     onResize : function(w, h){
14100 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
14101 //        if(typeof w == 'number'){
14102 //            var x = w - this.trigger.getWidth();
14103 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14104 //            this.trigger.setStyle('left', x+'px');
14105 //        }
14106     },
14107
14108     // private
14109     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14110
14111     // private
14112     getResizeEl : function(){
14113         return this.inputEl();
14114     },
14115
14116     // private
14117     getPositionEl : function(){
14118         return this.inputEl();
14119     },
14120
14121     // private
14122     alignErrorIcon : function(){
14123         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14124     },
14125
14126     // private
14127     initEvents : function(){
14128         
14129         this.createList();
14130         
14131         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
14132         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14133         if(!this.multiple && this.showToggleBtn){
14134             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14135             if(this.hideTrigger){
14136                 this.trigger.setDisplayed(false);
14137             }
14138             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14139         }
14140         
14141         if(this.multiple){
14142             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14143         }
14144         
14145         if(this.removable && !this.editable && !this.tickable){
14146             var close = this.closeTriggerEl();
14147             
14148             if(close){
14149                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14150                 close.on('click', this.removeBtnClick, this, close);
14151             }
14152         }
14153         
14154         //this.trigger.addClassOnOver('x-form-trigger-over');
14155         //this.trigger.addClassOnClick('x-form-trigger-click');
14156         
14157         //if(!this.width){
14158         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14159         //}
14160     },
14161     
14162     closeTriggerEl : function()
14163     {
14164         var close = this.el.select('.roo-combo-removable-btn', true).first();
14165         return close ? close : false;
14166     },
14167     
14168     removeBtnClick : function(e, h, el)
14169     {
14170         e.preventDefault();
14171         
14172         if(this.fireEvent("remove", this) !== false){
14173             this.reset();
14174             this.fireEvent("afterremove", this)
14175         }
14176     },
14177     
14178     createList : function()
14179     {
14180         this.list = Roo.get(document.body).createChild({
14181             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14182             cls: 'typeahead typeahead-long dropdown-menu shadow',
14183             style: 'display:none'
14184         });
14185         
14186         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14187         
14188     },
14189
14190     // private
14191     initTrigger : function(){
14192        
14193     },
14194
14195     // private
14196     onDestroy : function(){
14197         if(this.trigger){
14198             this.trigger.removeAllListeners();
14199           //  this.trigger.remove();
14200         }
14201         //if(this.wrap){
14202         //    this.wrap.remove();
14203         //}
14204         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
14205     },
14206
14207     // private
14208     onFocus : function(){
14209         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
14210         /*
14211         if(!this.mimicing){
14212             this.wrap.addClass('x-trigger-wrap-focus');
14213             this.mimicing = true;
14214             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14215             if(this.monitorTab){
14216                 this.el.on("keydown", this.checkTab, this);
14217             }
14218         }
14219         */
14220     },
14221
14222     // private
14223     checkTab : function(e){
14224         if(e.getKey() == e.TAB){
14225             this.triggerBlur();
14226         }
14227     },
14228
14229     // private
14230     onBlur : function(){
14231         // do nothing
14232     },
14233
14234     // private
14235     mimicBlur : function(e, t){
14236         /*
14237         if(!this.wrap.contains(t) && this.validateBlur()){
14238             this.triggerBlur();
14239         }
14240         */
14241     },
14242
14243     // private
14244     triggerBlur : function(){
14245         this.mimicing = false;
14246         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14247         if(this.monitorTab){
14248             this.el.un("keydown", this.checkTab, this);
14249         }
14250         //this.wrap.removeClass('x-trigger-wrap-focus');
14251         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
14252     },
14253
14254     // private
14255     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14256     validateBlur : function(e, t){
14257         return true;
14258     },
14259
14260     // private
14261     onDisable : function(){
14262         this.inputEl().dom.disabled = true;
14263         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
14264         //if(this.wrap){
14265         //    this.wrap.addClass('x-item-disabled');
14266         //}
14267     },
14268
14269     // private
14270     onEnable : function(){
14271         this.inputEl().dom.disabled = false;
14272         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
14273         //if(this.wrap){
14274         //    this.el.removeClass('x-item-disabled');
14275         //}
14276     },
14277
14278     // private
14279     onShow : function(){
14280         var ae = this.getActionEl();
14281         
14282         if(ae){
14283             ae.dom.style.display = '';
14284             ae.dom.style.visibility = 'visible';
14285         }
14286     },
14287
14288     // private
14289     
14290     onHide : function(){
14291         var ae = this.getActionEl();
14292         ae.dom.style.display = 'none';
14293     },
14294
14295     /**
14296      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14297      * by an implementing function.
14298      * @method
14299      * @param {EventObject} e
14300      */
14301     onTriggerClick : Roo.emptyFn
14302 });
14303  
14304 /*
14305 * Licence: LGPL
14306 */
14307
14308 /**
14309  * @class Roo.bootstrap.CardUploader
14310  * @extends Roo.bootstrap.Button
14311  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14312  * @cfg {Number} errorTimeout default 3000
14313  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14314  * @cfg {Array}  html The button text.
14315
14316  *
14317  * @constructor
14318  * Create a new CardUploader
14319  * @param {Object} config The config object
14320  */
14321
14322 Roo.bootstrap.CardUploader = function(config){
14323     
14324  
14325     
14326     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
14327     
14328     
14329     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14330         return r.data.id
14331      });
14332     
14333      this.addEvents({
14334          // raw events
14335         /**
14336          * @event preview
14337          * When a image is clicked on - and needs to display a slideshow or similar..
14338          * @param {Roo.bootstrap.Card} this
14339          * @param {Object} The image information data 
14340          *
14341          */
14342         'preview' : true,
14343          /**
14344          * @event download
14345          * When a the download link is clicked
14346          * @param {Roo.bootstrap.Card} this
14347          * @param {Object} The image information data  contains 
14348          */
14349         'download' : true
14350         
14351     });
14352 };
14353  
14354 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
14355     
14356      
14357     errorTimeout : 3000,
14358      
14359     images : false,
14360    
14361     fileCollection : false,
14362     allowBlank : true,
14363     
14364     getAutoCreate : function()
14365     {
14366         
14367         var cfg =  {
14368             cls :'form-group' ,
14369             cn : [
14370                
14371                 {
14372                     tag: 'label',
14373                    //cls : 'input-group-addon',
14374                     html : this.fieldLabel
14375
14376                 },
14377
14378                 {
14379                     tag: 'input',
14380                     type : 'hidden',
14381                     name : this.name,
14382                     value : this.value,
14383                     cls : 'd-none  form-control'
14384                 },
14385                 
14386                 {
14387                     tag: 'input',
14388                     multiple : 'multiple',
14389                     type : 'file',
14390                     cls : 'd-none  roo-card-upload-selector'
14391                 },
14392                 
14393                 {
14394                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14395                 },
14396                 {
14397                     cls : 'card-columns roo-card-uploader-container'
14398                 }
14399
14400             ]
14401         };
14402            
14403          
14404         return cfg;
14405     },
14406     
14407     getChildContainer : function() /// what children are added to.
14408     {
14409         return this.containerEl;
14410     },
14411    
14412     getButtonContainer : function() /// what children are added to.
14413     {
14414         return this.el.select(".roo-card-uploader-button-container").first();
14415     },
14416    
14417     initEvents : function()
14418     {
14419         
14420         Roo.bootstrap.Input.prototype.initEvents.call(this);
14421         
14422         var t = this;
14423         this.addxtype({
14424             xns: Roo.bootstrap,
14425
14426             xtype : 'Button',
14427             container_method : 'getButtonContainer' ,            
14428             html :  this.html, // fix changable?
14429             cls : 'w-100 ',
14430             listeners : {
14431                 'click' : function(btn, e) {
14432                     t.onClick(e);
14433                 }
14434             }
14435         });
14436         
14437         
14438         
14439         
14440         this.urlAPI = (window.createObjectURL && window) || 
14441                                 (window.URL && URL.revokeObjectURL && URL) || 
14442                                 (window.webkitURL && webkitURL);
14443                         
14444          
14445          
14446          
14447         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14448         
14449         this.selectorEl.on('change', this.onFileSelected, this);
14450         if (this.images) {
14451             var t = this;
14452             this.images.forEach(function(img) {
14453                 t.addCard(img)
14454             });
14455             this.images = false;
14456         }
14457         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14458          
14459        
14460     },
14461     
14462    
14463     onClick : function(e)
14464     {
14465         e.preventDefault();
14466          
14467         this.selectorEl.dom.click();
14468          
14469     },
14470     
14471     onFileSelected : function(e)
14472     {
14473         e.preventDefault();
14474         
14475         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14476             return;
14477         }
14478         
14479         Roo.each(this.selectorEl.dom.files, function(file){    
14480             this.addFile(file);
14481         }, this);
14482          
14483     },
14484     
14485       
14486     
14487       
14488     
14489     addFile : function(file)
14490     {
14491            
14492         if(typeof(file) === 'string'){
14493             throw "Add file by name?"; // should not happen
14494             return;
14495         }
14496         
14497         if(!file || !this.urlAPI){
14498             return;
14499         }
14500         
14501         // file;
14502         // file.type;
14503         
14504         var _this = this;
14505         
14506         
14507         var url = _this.urlAPI.createObjectURL( file);
14508            
14509         this.addCard({
14510             id : Roo.bootstrap.CardUploader.ID--,
14511             is_uploaded : false,
14512             src : url,
14513             srcfile : file,
14514             title : file.name,
14515             mimetype : file.type,
14516             preview : false,
14517             is_deleted : 0
14518         });
14519         
14520     },
14521     
14522     /**
14523      * addCard - add an Attachment to the uploader
14524      * @param data - the data about the image to upload
14525      *
14526      * {
14527           id : 123
14528           title : "Title of file",
14529           is_uploaded : false,
14530           src : "http://.....",
14531           srcfile : { the File upload object },
14532           mimetype : file.type,
14533           preview : false,
14534           is_deleted : 0
14535           .. any other data...
14536         }
14537      *
14538      * 
14539     */
14540     
14541     addCard : function (data)
14542     {
14543         // hidden input element?
14544         // if the file is not an image...
14545         //then we need to use something other that and header_image
14546         var t = this;
14547         //   remove.....
14548         var footer = [
14549             {
14550                 xns : Roo.bootstrap,
14551                 xtype : 'CardFooter',
14552                  items: [
14553                     {
14554                         xns : Roo.bootstrap,
14555                         xtype : 'Element',
14556                         cls : 'd-flex',
14557                         items : [
14558                             
14559                             {
14560                                 xns : Roo.bootstrap,
14561                                 xtype : 'Button',
14562                                 html : String.format("<small>{0}</small>", data.title),
14563                                 cls : 'col-10 text-left',
14564                                 size: 'sm',
14565                                 weight: 'link',
14566                                 fa : 'download',
14567                                 listeners : {
14568                                     click : function() {
14569                                      
14570                                         t.fireEvent( "download", t, data );
14571                                     }
14572                                 }
14573                             },
14574                           
14575                             {
14576                                 xns : Roo.bootstrap,
14577                                 xtype : 'Button',
14578                                 style: 'max-height: 28px; ',
14579                                 size : 'sm',
14580                                 weight: 'danger',
14581                                 cls : 'col-2',
14582                                 fa : 'times',
14583                                 listeners : {
14584                                     click : function() {
14585                                         t.removeCard(data.id)
14586                                     }
14587                                 }
14588                             }
14589                         ]
14590                     }
14591                     
14592                 ] 
14593             }
14594             
14595         ];
14596         
14597         var cn = this.addxtype(
14598             {
14599                  
14600                 xns : Roo.bootstrap,
14601                 xtype : 'Card',
14602                 closeable : true,
14603                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14604                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14605                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14606                 data : data,
14607                 html : false,
14608                  
14609                 items : footer,
14610                 initEvents : function() {
14611                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14612                     var card = this;
14613                     this.imgEl = this.el.select('.card-img-top').first();
14614                     if (this.imgEl) {
14615                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14616                         this.imgEl.set({ 'pointer' : 'cursor' });
14617                                   
14618                     }
14619                     this.getCardFooter().addClass('p-1');
14620                     
14621                   
14622                 }
14623                 
14624             }
14625         );
14626         // dont' really need ot update items.
14627         // this.items.push(cn);
14628         this.fileCollection.add(cn);
14629         
14630         if (!data.srcfile) {
14631             this.updateInput();
14632             return;
14633         }
14634             
14635         var _t = this;
14636         var reader = new FileReader();
14637         reader.addEventListener("load", function() {  
14638             data.srcdata =  reader.result;
14639             _t.updateInput();
14640         });
14641         reader.readAsDataURL(data.srcfile);
14642         
14643         
14644         
14645     },
14646     removeCard : function(id)
14647     {
14648         
14649         var card  = this.fileCollection.get(id);
14650         card.data.is_deleted = 1;
14651         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14652         //this.fileCollection.remove(card);
14653         //this.items = this.items.filter(function(e) { return e != card });
14654         // dont' really need ot update items.
14655         card.el.dom.parentNode.removeChild(card.el.dom);
14656         this.updateInput();
14657
14658         
14659     },
14660     reset: function()
14661     {
14662         this.fileCollection.each(function(card) {
14663             if (card.el.dom && card.el.dom.parentNode) {
14664                 card.el.dom.parentNode.removeChild(card.el.dom);
14665             }
14666         });
14667         this.fileCollection.clear();
14668         this.updateInput();
14669     },
14670     
14671     updateInput : function()
14672     {
14673          var data = [];
14674         this.fileCollection.each(function(e) {
14675             data.push(e.data);
14676             
14677         });
14678         this.inputEl().dom.value = JSON.stringify(data);
14679         
14680         
14681         
14682     }
14683     
14684     
14685 });
14686
14687
14688 Roo.bootstrap.CardUploader.ID = -1;/*
14689  * Based on:
14690  * Ext JS Library 1.1.1
14691  * Copyright(c) 2006-2007, Ext JS, LLC.
14692  *
14693  * Originally Released Under LGPL - original licence link has changed is not relivant.
14694  *
14695  * Fork - LGPL
14696  * <script type="text/javascript">
14697  */
14698
14699
14700 /**
14701  * @class Roo.data.SortTypes
14702  * @singleton
14703  * Defines the default sorting (casting?) comparison functions used when sorting data.
14704  */
14705 Roo.data.SortTypes = {
14706     /**
14707      * Default sort that does nothing
14708      * @param {Mixed} s The value being converted
14709      * @return {Mixed} The comparison value
14710      */
14711     none : function(s){
14712         return s;
14713     },
14714     
14715     /**
14716      * The regular expression used to strip tags
14717      * @type {RegExp}
14718      * @property
14719      */
14720     stripTagsRE : /<\/?[^>]+>/gi,
14721     
14722     /**
14723      * Strips all HTML tags to sort on text only
14724      * @param {Mixed} s The value being converted
14725      * @return {String} The comparison value
14726      */
14727     asText : function(s){
14728         return String(s).replace(this.stripTagsRE, "");
14729     },
14730     
14731     /**
14732      * Strips all HTML tags to sort on text only - Case insensitive
14733      * @param {Mixed} s The value being converted
14734      * @return {String} The comparison value
14735      */
14736     asUCText : function(s){
14737         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14738     },
14739     
14740     /**
14741      * Case insensitive string
14742      * @param {Mixed} s The value being converted
14743      * @return {String} The comparison value
14744      */
14745     asUCString : function(s) {
14746         return String(s).toUpperCase();
14747     },
14748     
14749     /**
14750      * Date sorting
14751      * @param {Mixed} s The value being converted
14752      * @return {Number} The comparison value
14753      */
14754     asDate : function(s) {
14755         if(!s){
14756             return 0;
14757         }
14758         if(s instanceof Date){
14759             return s.getTime();
14760         }
14761         return Date.parse(String(s));
14762     },
14763     
14764     /**
14765      * Float sorting
14766      * @param {Mixed} s The value being converted
14767      * @return {Float} The comparison value
14768      */
14769     asFloat : function(s) {
14770         var val = parseFloat(String(s).replace(/,/g, ""));
14771         if(isNaN(val)) {
14772             val = 0;
14773         }
14774         return val;
14775     },
14776     
14777     /**
14778      * Integer sorting
14779      * @param {Mixed} s The value being converted
14780      * @return {Number} The comparison value
14781      */
14782     asInt : function(s) {
14783         var val = parseInt(String(s).replace(/,/g, ""));
14784         if(isNaN(val)) {
14785             val = 0;
14786         }
14787         return val;
14788     }
14789 };/*
14790  * Based on:
14791  * Ext JS Library 1.1.1
14792  * Copyright(c) 2006-2007, Ext JS, LLC.
14793  *
14794  * Originally Released Under LGPL - original licence link has changed is not relivant.
14795  *
14796  * Fork - LGPL
14797  * <script type="text/javascript">
14798  */
14799
14800 /**
14801 * @class Roo.data.Record
14802  * Instances of this class encapsulate both record <em>definition</em> information, and record
14803  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14804  * to access Records cached in an {@link Roo.data.Store} object.<br>
14805  * <p>
14806  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14807  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14808  * objects.<br>
14809  * <p>
14810  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14811  * @constructor
14812  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14813  * {@link #create}. The parameters are the same.
14814  * @param {Array} data An associative Array of data values keyed by the field name.
14815  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14816  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14817  * not specified an integer id is generated.
14818  */
14819 Roo.data.Record = function(data, id){
14820     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14821     this.data = data;
14822 };
14823
14824 /**
14825  * Generate a constructor for a specific record layout.
14826  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14827  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14828  * Each field definition object may contain the following properties: <ul>
14829  * <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,
14830  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14831  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14832  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14833  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14834  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14835  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14836  * this may be omitted.</p></li>
14837  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14838  * <ul><li>auto (Default, implies no conversion)</li>
14839  * <li>string</li>
14840  * <li>int</li>
14841  * <li>float</li>
14842  * <li>boolean</li>
14843  * <li>date</li></ul></p></li>
14844  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14845  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14846  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14847  * by the Reader into an object that will be stored in the Record. It is passed the
14848  * following parameters:<ul>
14849  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14850  * </ul></p></li>
14851  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14852  * </ul>
14853  * <br>usage:<br><pre><code>
14854 var TopicRecord = Roo.data.Record.create(
14855     {name: 'title', mapping: 'topic_title'},
14856     {name: 'author', mapping: 'username'},
14857     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14858     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14859     {name: 'lastPoster', mapping: 'user2'},
14860     {name: 'excerpt', mapping: 'post_text'}
14861 );
14862
14863 var myNewRecord = new TopicRecord({
14864     title: 'Do my job please',
14865     author: 'noobie',
14866     totalPosts: 1,
14867     lastPost: new Date(),
14868     lastPoster: 'Animal',
14869     excerpt: 'No way dude!'
14870 });
14871 myStore.add(myNewRecord);
14872 </code></pre>
14873  * @method create
14874  * @static
14875  */
14876 Roo.data.Record.create = function(o){
14877     var f = function(){
14878         f.superclass.constructor.apply(this, arguments);
14879     };
14880     Roo.extend(f, Roo.data.Record);
14881     var p = f.prototype;
14882     p.fields = new Roo.util.MixedCollection(false, function(field){
14883         return field.name;
14884     });
14885     for(var i = 0, len = o.length; i < len; i++){
14886         p.fields.add(new Roo.data.Field(o[i]));
14887     }
14888     f.getField = function(name){
14889         return p.fields.get(name);  
14890     };
14891     return f;
14892 };
14893
14894 Roo.data.Record.AUTO_ID = 1000;
14895 Roo.data.Record.EDIT = 'edit';
14896 Roo.data.Record.REJECT = 'reject';
14897 Roo.data.Record.COMMIT = 'commit';
14898
14899 Roo.data.Record.prototype = {
14900     /**
14901      * Readonly flag - true if this record has been modified.
14902      * @type Boolean
14903      */
14904     dirty : false,
14905     editing : false,
14906     error: null,
14907     modified: null,
14908
14909     // private
14910     join : function(store){
14911         this.store = store;
14912     },
14913
14914     /**
14915      * Set the named field to the specified value.
14916      * @param {String} name The name of the field to set.
14917      * @param {Object} value The value to set the field to.
14918      */
14919     set : function(name, value){
14920         if(this.data[name] == value){
14921             return;
14922         }
14923         this.dirty = true;
14924         if(!this.modified){
14925             this.modified = {};
14926         }
14927         if(typeof this.modified[name] == 'undefined'){
14928             this.modified[name] = this.data[name];
14929         }
14930         this.data[name] = value;
14931         if(!this.editing && this.store){
14932             this.store.afterEdit(this);
14933         }       
14934     },
14935
14936     /**
14937      * Get the value of the named field.
14938      * @param {String} name The name of the field to get the value of.
14939      * @return {Object} The value of the field.
14940      */
14941     get : function(name){
14942         return this.data[name]; 
14943     },
14944
14945     // private
14946     beginEdit : function(){
14947         this.editing = true;
14948         this.modified = {}; 
14949     },
14950
14951     // private
14952     cancelEdit : function(){
14953         this.editing = false;
14954         delete this.modified;
14955     },
14956
14957     // private
14958     endEdit : function(){
14959         this.editing = false;
14960         if(this.dirty && this.store){
14961             this.store.afterEdit(this);
14962         }
14963     },
14964
14965     /**
14966      * Usually called by the {@link Roo.data.Store} which owns the Record.
14967      * Rejects all changes made to the Record since either creation, or the last commit operation.
14968      * Modified fields are reverted to their original values.
14969      * <p>
14970      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14971      * of reject operations.
14972      */
14973     reject : function(){
14974         var m = this.modified;
14975         for(var n in m){
14976             if(typeof m[n] != "function"){
14977                 this.data[n] = m[n];
14978             }
14979         }
14980         this.dirty = false;
14981         delete this.modified;
14982         this.editing = false;
14983         if(this.store){
14984             this.store.afterReject(this);
14985         }
14986     },
14987
14988     /**
14989      * Usually called by the {@link Roo.data.Store} which owns the Record.
14990      * Commits all changes made to the Record since either creation, or the last commit operation.
14991      * <p>
14992      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14993      * of commit operations.
14994      */
14995     commit : function(){
14996         this.dirty = false;
14997         delete this.modified;
14998         this.editing = false;
14999         if(this.store){
15000             this.store.afterCommit(this);
15001         }
15002     },
15003
15004     // private
15005     hasError : function(){
15006         return this.error != null;
15007     },
15008
15009     // private
15010     clearError : function(){
15011         this.error = null;
15012     },
15013
15014     /**
15015      * Creates a copy of this record.
15016      * @param {String} id (optional) A new record id if you don't want to use this record's id
15017      * @return {Record}
15018      */
15019     copy : function(newId) {
15020         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15021     }
15022 };/*
15023  * Based on:
15024  * Ext JS Library 1.1.1
15025  * Copyright(c) 2006-2007, Ext JS, LLC.
15026  *
15027  * Originally Released Under LGPL - original licence link has changed is not relivant.
15028  *
15029  * Fork - LGPL
15030  * <script type="text/javascript">
15031  */
15032
15033
15034
15035 /**
15036  * @class Roo.data.Store
15037  * @extends Roo.util.Observable
15038  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15039  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15040  * <p>
15041  * 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
15042  * has no knowledge of the format of the data returned by the Proxy.<br>
15043  * <p>
15044  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15045  * instances from the data object. These records are cached and made available through accessor functions.
15046  * @constructor
15047  * Creates a new Store.
15048  * @param {Object} config A config object containing the objects needed for the Store to access data,
15049  * and read the data into Records.
15050  */
15051 Roo.data.Store = function(config){
15052     this.data = new Roo.util.MixedCollection(false);
15053     this.data.getKey = function(o){
15054         return o.id;
15055     };
15056     this.baseParams = {};
15057     // private
15058     this.paramNames = {
15059         "start" : "start",
15060         "limit" : "limit",
15061         "sort" : "sort",
15062         "dir" : "dir",
15063         "multisort" : "_multisort"
15064     };
15065
15066     if(config && config.data){
15067         this.inlineData = config.data;
15068         delete config.data;
15069     }
15070
15071     Roo.apply(this, config);
15072     
15073     if(this.reader){ // reader passed
15074         this.reader = Roo.factory(this.reader, Roo.data);
15075         this.reader.xmodule = this.xmodule || false;
15076         if(!this.recordType){
15077             this.recordType = this.reader.recordType;
15078         }
15079         if(this.reader.onMetaChange){
15080             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15081         }
15082     }
15083
15084     if(this.recordType){
15085         this.fields = this.recordType.prototype.fields;
15086     }
15087     this.modified = [];
15088
15089     this.addEvents({
15090         /**
15091          * @event datachanged
15092          * Fires when the data cache has changed, and a widget which is using this Store
15093          * as a Record cache should refresh its view.
15094          * @param {Store} this
15095          */
15096         datachanged : true,
15097         /**
15098          * @event metachange
15099          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15100          * @param {Store} this
15101          * @param {Object} meta The JSON metadata
15102          */
15103         metachange : true,
15104         /**
15105          * @event add
15106          * Fires when Records have been added to the Store
15107          * @param {Store} this
15108          * @param {Roo.data.Record[]} records The array of Records added
15109          * @param {Number} index The index at which the record(s) were added
15110          */
15111         add : true,
15112         /**
15113          * @event remove
15114          * Fires when a Record has been removed from the Store
15115          * @param {Store} this
15116          * @param {Roo.data.Record} record The Record that was removed
15117          * @param {Number} index The index at which the record was removed
15118          */
15119         remove : true,
15120         /**
15121          * @event update
15122          * Fires when a Record has been updated
15123          * @param {Store} this
15124          * @param {Roo.data.Record} record The Record that was updated
15125          * @param {String} operation The update operation being performed.  Value may be one of:
15126          * <pre><code>
15127  Roo.data.Record.EDIT
15128  Roo.data.Record.REJECT
15129  Roo.data.Record.COMMIT
15130          * </code></pre>
15131          */
15132         update : true,
15133         /**
15134          * @event clear
15135          * Fires when the data cache has been cleared.
15136          * @param {Store} this
15137          */
15138         clear : true,
15139         /**
15140          * @event beforeload
15141          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15142          * the load action will be canceled.
15143          * @param {Store} this
15144          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15145          */
15146         beforeload : true,
15147         /**
15148          * @event beforeloadadd
15149          * Fires after a new set of Records has been loaded.
15150          * @param {Store} this
15151          * @param {Roo.data.Record[]} records The Records that were loaded
15152          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15153          */
15154         beforeloadadd : true,
15155         /**
15156          * @event load
15157          * Fires after a new set of Records has been loaded, before they are added to the store.
15158          * @param {Store} this
15159          * @param {Roo.data.Record[]} records The Records that were loaded
15160          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15161          * @params {Object} return from reader
15162          */
15163         load : true,
15164         /**
15165          * @event loadexception
15166          * Fires if an exception occurs in the Proxy during loading.
15167          * Called with the signature of the Proxy's "loadexception" event.
15168          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15169          * 
15170          * @param {Proxy} 
15171          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15172          * @param {Object} load options 
15173          * @param {Object} jsonData from your request (normally this contains the Exception)
15174          */
15175         loadexception : true
15176     });
15177     
15178     if(this.proxy){
15179         this.proxy = Roo.factory(this.proxy, Roo.data);
15180         this.proxy.xmodule = this.xmodule || false;
15181         this.relayEvents(this.proxy,  ["loadexception"]);
15182     }
15183     this.sortToggle = {};
15184     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15185
15186     Roo.data.Store.superclass.constructor.call(this);
15187
15188     if(this.inlineData){
15189         this.loadData(this.inlineData);
15190         delete this.inlineData;
15191     }
15192 };
15193
15194 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15195      /**
15196     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15197     * without a remote query - used by combo/forms at present.
15198     */
15199     
15200     /**
15201     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15202     */
15203     /**
15204     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15205     */
15206     /**
15207     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15208     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15209     */
15210     /**
15211     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15212     * on any HTTP request
15213     */
15214     /**
15215     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15216     */
15217     /**
15218     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15219     */
15220     multiSort: false,
15221     /**
15222     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15223     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15224     */
15225     remoteSort : false,
15226
15227     /**
15228     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15229      * loaded or when a record is removed. (defaults to false).
15230     */
15231     pruneModifiedRecords : false,
15232
15233     // private
15234     lastOptions : null,
15235
15236     /**
15237      * Add Records to the Store and fires the add event.
15238      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15239      */
15240     add : function(records){
15241         records = [].concat(records);
15242         for(var i = 0, len = records.length; i < len; i++){
15243             records[i].join(this);
15244         }
15245         var index = this.data.length;
15246         this.data.addAll(records);
15247         this.fireEvent("add", this, records, index);
15248     },
15249
15250     /**
15251      * Remove a Record from the Store and fires the remove event.
15252      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15253      */
15254     remove : function(record){
15255         var index = this.data.indexOf(record);
15256         this.data.removeAt(index);
15257  
15258         if(this.pruneModifiedRecords){
15259             this.modified.remove(record);
15260         }
15261         this.fireEvent("remove", this, record, index);
15262     },
15263
15264     /**
15265      * Remove all Records from the Store and fires the clear event.
15266      */
15267     removeAll : function(){
15268         this.data.clear();
15269         if(this.pruneModifiedRecords){
15270             this.modified = [];
15271         }
15272         this.fireEvent("clear", this);
15273     },
15274
15275     /**
15276      * Inserts Records to the Store at the given index and fires the add event.
15277      * @param {Number} index The start index at which to insert the passed Records.
15278      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15279      */
15280     insert : function(index, records){
15281         records = [].concat(records);
15282         for(var i = 0, len = records.length; i < len; i++){
15283             this.data.insert(index, records[i]);
15284             records[i].join(this);
15285         }
15286         this.fireEvent("add", this, records, index);
15287     },
15288
15289     /**
15290      * Get the index within the cache of the passed Record.
15291      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15292      * @return {Number} The index of the passed Record. Returns -1 if not found.
15293      */
15294     indexOf : function(record){
15295         return this.data.indexOf(record);
15296     },
15297
15298     /**
15299      * Get the index within the cache of the Record with the passed id.
15300      * @param {String} id The id of the Record to find.
15301      * @return {Number} The index of the Record. Returns -1 if not found.
15302      */
15303     indexOfId : function(id){
15304         return this.data.indexOfKey(id);
15305     },
15306
15307     /**
15308      * Get the Record with the specified id.
15309      * @param {String} id The id of the Record to find.
15310      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15311      */
15312     getById : function(id){
15313         return this.data.key(id);
15314     },
15315
15316     /**
15317      * Get the Record at the specified index.
15318      * @param {Number} index The index of the Record to find.
15319      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15320      */
15321     getAt : function(index){
15322         return this.data.itemAt(index);
15323     },
15324
15325     /**
15326      * Returns a range of Records between specified indices.
15327      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15328      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15329      * @return {Roo.data.Record[]} An array of Records
15330      */
15331     getRange : function(start, end){
15332         return this.data.getRange(start, end);
15333     },
15334
15335     // private
15336     storeOptions : function(o){
15337         o = Roo.apply({}, o);
15338         delete o.callback;
15339         delete o.scope;
15340         this.lastOptions = o;
15341     },
15342
15343     /**
15344      * Loads the Record cache from the configured Proxy using the configured Reader.
15345      * <p>
15346      * If using remote paging, then the first load call must specify the <em>start</em>
15347      * and <em>limit</em> properties in the options.params property to establish the initial
15348      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15349      * <p>
15350      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15351      * and this call will return before the new data has been loaded. Perform any post-processing
15352      * in a callback function, or in a "load" event handler.</strong>
15353      * <p>
15354      * @param {Object} options An object containing properties which control loading options:<ul>
15355      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15356      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15357      * passed the following arguments:<ul>
15358      * <li>r : Roo.data.Record[]</li>
15359      * <li>options: Options object from the load call</li>
15360      * <li>success: Boolean success indicator</li></ul></li>
15361      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15362      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15363      * </ul>
15364      */
15365     load : function(options){
15366         options = options || {};
15367         if(this.fireEvent("beforeload", this, options) !== false){
15368             this.storeOptions(options);
15369             var p = Roo.apply(options.params || {}, this.baseParams);
15370             // if meta was not loaded from remote source.. try requesting it.
15371             if (!this.reader.metaFromRemote) {
15372                 p._requestMeta = 1;
15373             }
15374             if(this.sortInfo && this.remoteSort){
15375                 var pn = this.paramNames;
15376                 p[pn["sort"]] = this.sortInfo.field;
15377                 p[pn["dir"]] = this.sortInfo.direction;
15378             }
15379             if (this.multiSort) {
15380                 var pn = this.paramNames;
15381                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15382             }
15383             
15384             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15385         }
15386     },
15387
15388     /**
15389      * Reloads the Record cache from the configured Proxy using the configured Reader and
15390      * the options from the last load operation performed.
15391      * @param {Object} options (optional) An object containing properties which may override the options
15392      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15393      * the most recently used options are reused).
15394      */
15395     reload : function(options){
15396         this.load(Roo.applyIf(options||{}, this.lastOptions));
15397     },
15398
15399     // private
15400     // Called as a callback by the Reader during a load operation.
15401     loadRecords : function(o, options, success){
15402         if(!o || success === false){
15403             if(success !== false){
15404                 this.fireEvent("load", this, [], options, o);
15405             }
15406             if(options.callback){
15407                 options.callback.call(options.scope || this, [], options, false);
15408             }
15409             return;
15410         }
15411         // if data returned failure - throw an exception.
15412         if (o.success === false) {
15413             // show a message if no listener is registered.
15414             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15415                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15416             }
15417             // loadmask wil be hooked into this..
15418             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15419             return;
15420         }
15421         var r = o.records, t = o.totalRecords || r.length;
15422         
15423         this.fireEvent("beforeloadadd", this, r, options, o);
15424         
15425         if(!options || options.add !== true){
15426             if(this.pruneModifiedRecords){
15427                 this.modified = [];
15428             }
15429             for(var i = 0, len = r.length; i < len; i++){
15430                 r[i].join(this);
15431             }
15432             if(this.snapshot){
15433                 this.data = this.snapshot;
15434                 delete this.snapshot;
15435             }
15436             this.data.clear();
15437             this.data.addAll(r);
15438             this.totalLength = t;
15439             this.applySort();
15440             this.fireEvent("datachanged", this);
15441         }else{
15442             this.totalLength = Math.max(t, this.data.length+r.length);
15443             this.add(r);
15444         }
15445         
15446         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15447                 
15448             var e = new Roo.data.Record({});
15449
15450             e.set(this.parent.displayField, this.parent.emptyTitle);
15451             e.set(this.parent.valueField, '');
15452
15453             this.insert(0, e);
15454         }
15455             
15456         this.fireEvent("load", this, r, options, o);
15457         if(options.callback){
15458             options.callback.call(options.scope || this, r, options, true);
15459         }
15460     },
15461
15462
15463     /**
15464      * Loads data from a passed data block. A Reader which understands the format of the data
15465      * must have been configured in the constructor.
15466      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15467      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15468      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15469      */
15470     loadData : function(o, append){
15471         var r = this.reader.readRecords(o);
15472         this.loadRecords(r, {add: append}, true);
15473     },
15474     
15475      /**
15476      * using 'cn' the nested child reader read the child array into it's child stores.
15477      * @param {Object} rec The record with a 'children array
15478      */
15479     loadDataFromChildren : function(rec)
15480     {
15481         this.loadData(this.reader.toLoadData(rec));
15482     },
15483     
15484
15485     /**
15486      * Gets the number of cached records.
15487      * <p>
15488      * <em>If using paging, this may not be the total size of the dataset. If the data object
15489      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15490      * the data set size</em>
15491      */
15492     getCount : function(){
15493         return this.data.length || 0;
15494     },
15495
15496     /**
15497      * Gets the total number of records in the dataset as returned by the server.
15498      * <p>
15499      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15500      * the dataset size</em>
15501      */
15502     getTotalCount : function(){
15503         return this.totalLength || 0;
15504     },
15505
15506     /**
15507      * Returns the sort state of the Store as an object with two properties:
15508      * <pre><code>
15509  field {String} The name of the field by which the Records are sorted
15510  direction {String} The sort order, "ASC" or "DESC"
15511      * </code></pre>
15512      */
15513     getSortState : function(){
15514         return this.sortInfo;
15515     },
15516
15517     // private
15518     applySort : function(){
15519         if(this.sortInfo && !this.remoteSort){
15520             var s = this.sortInfo, f = s.field;
15521             var st = this.fields.get(f).sortType;
15522             var fn = function(r1, r2){
15523                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15524                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15525             };
15526             this.data.sort(s.direction, fn);
15527             if(this.snapshot && this.snapshot != this.data){
15528                 this.snapshot.sort(s.direction, fn);
15529             }
15530         }
15531     },
15532
15533     /**
15534      * Sets the default sort column and order to be used by the next load operation.
15535      * @param {String} fieldName The name of the field to sort by.
15536      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15537      */
15538     setDefaultSort : function(field, dir){
15539         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15540     },
15541
15542     /**
15543      * Sort the Records.
15544      * If remote sorting is used, the sort is performed on the server, and the cache is
15545      * reloaded. If local sorting is used, the cache is sorted internally.
15546      * @param {String} fieldName The name of the field to sort by.
15547      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15548      */
15549     sort : function(fieldName, dir){
15550         var f = this.fields.get(fieldName);
15551         if(!dir){
15552             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15553             
15554             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15555                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15556             }else{
15557                 dir = f.sortDir;
15558             }
15559         }
15560         this.sortToggle[f.name] = dir;
15561         this.sortInfo = {field: f.name, direction: dir};
15562         if(!this.remoteSort){
15563             this.applySort();
15564             this.fireEvent("datachanged", this);
15565         }else{
15566             this.load(this.lastOptions);
15567         }
15568     },
15569
15570     /**
15571      * Calls the specified function for each of the Records in the cache.
15572      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15573      * Returning <em>false</em> aborts and exits the iteration.
15574      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15575      */
15576     each : function(fn, scope){
15577         this.data.each(fn, scope);
15578     },
15579
15580     /**
15581      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15582      * (e.g., during paging).
15583      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15584      */
15585     getModifiedRecords : function(){
15586         return this.modified;
15587     },
15588
15589     // private
15590     createFilterFn : function(property, value, anyMatch){
15591         if(!value.exec){ // not a regex
15592             value = String(value);
15593             if(value.length == 0){
15594                 return false;
15595             }
15596             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15597         }
15598         return function(r){
15599             return value.test(r.data[property]);
15600         };
15601     },
15602
15603     /**
15604      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15605      * @param {String} property A field on your records
15606      * @param {Number} start The record index to start at (defaults to 0)
15607      * @param {Number} end The last record index to include (defaults to length - 1)
15608      * @return {Number} The sum
15609      */
15610     sum : function(property, start, end){
15611         var rs = this.data.items, v = 0;
15612         start = start || 0;
15613         end = (end || end === 0) ? end : rs.length-1;
15614
15615         for(var i = start; i <= end; i++){
15616             v += (rs[i].data[property] || 0);
15617         }
15618         return v;
15619     },
15620
15621     /**
15622      * Filter the records by a specified property.
15623      * @param {String} field A field on your records
15624      * @param {String/RegExp} value Either a string that the field
15625      * should start with or a RegExp to test against the field
15626      * @param {Boolean} anyMatch True to match any part not just the beginning
15627      */
15628     filter : function(property, value, anyMatch){
15629         var fn = this.createFilterFn(property, value, anyMatch);
15630         return fn ? this.filterBy(fn) : this.clearFilter();
15631     },
15632
15633     /**
15634      * Filter by a function. The specified function will be called with each
15635      * record in this data source. If the function returns true the record is included,
15636      * otherwise it is filtered.
15637      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15638      * @param {Object} scope (optional) The scope of the function (defaults to this)
15639      */
15640     filterBy : function(fn, scope){
15641         this.snapshot = this.snapshot || this.data;
15642         this.data = this.queryBy(fn, scope||this);
15643         this.fireEvent("datachanged", this);
15644     },
15645
15646     /**
15647      * Query the records by a specified property.
15648      * @param {String} field A field on your records
15649      * @param {String/RegExp} value Either a string that the field
15650      * should start with or a RegExp to test against the field
15651      * @param {Boolean} anyMatch True to match any part not just the beginning
15652      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15653      */
15654     query : function(property, value, anyMatch){
15655         var fn = this.createFilterFn(property, value, anyMatch);
15656         return fn ? this.queryBy(fn) : this.data.clone();
15657     },
15658
15659     /**
15660      * Query by a function. The specified function will be called with each
15661      * record in this data source. If the function returns true the record is included
15662      * in the results.
15663      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15664      * @param {Object} scope (optional) The scope of the function (defaults to this)
15665       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15666      **/
15667     queryBy : function(fn, scope){
15668         var data = this.snapshot || this.data;
15669         return data.filterBy(fn, scope||this);
15670     },
15671
15672     /**
15673      * Collects unique values for a particular dataIndex from this store.
15674      * @param {String} dataIndex The property to collect
15675      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15676      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15677      * @return {Array} An array of the unique values
15678      **/
15679     collect : function(dataIndex, allowNull, bypassFilter){
15680         var d = (bypassFilter === true && this.snapshot) ?
15681                 this.snapshot.items : this.data.items;
15682         var v, sv, r = [], l = {};
15683         for(var i = 0, len = d.length; i < len; i++){
15684             v = d[i].data[dataIndex];
15685             sv = String(v);
15686             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15687                 l[sv] = true;
15688                 r[r.length] = v;
15689             }
15690         }
15691         return r;
15692     },
15693
15694     /**
15695      * Revert to a view of the Record cache with no filtering applied.
15696      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15697      */
15698     clearFilter : function(suppressEvent){
15699         if(this.snapshot && this.snapshot != this.data){
15700             this.data = this.snapshot;
15701             delete this.snapshot;
15702             if(suppressEvent !== true){
15703                 this.fireEvent("datachanged", this);
15704             }
15705         }
15706     },
15707
15708     // private
15709     afterEdit : function(record){
15710         if(this.modified.indexOf(record) == -1){
15711             this.modified.push(record);
15712         }
15713         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15714     },
15715     
15716     // private
15717     afterReject : function(record){
15718         this.modified.remove(record);
15719         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15720     },
15721
15722     // private
15723     afterCommit : function(record){
15724         this.modified.remove(record);
15725         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15726     },
15727
15728     /**
15729      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15730      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15731      */
15732     commitChanges : function(){
15733         var m = this.modified.slice(0);
15734         this.modified = [];
15735         for(var i = 0, len = m.length; i < len; i++){
15736             m[i].commit();
15737         }
15738     },
15739
15740     /**
15741      * Cancel outstanding changes on all changed records.
15742      */
15743     rejectChanges : function(){
15744         var m = this.modified.slice(0);
15745         this.modified = [];
15746         for(var i = 0, len = m.length; i < len; i++){
15747             m[i].reject();
15748         }
15749     },
15750
15751     onMetaChange : function(meta, rtype, o){
15752         this.recordType = rtype;
15753         this.fields = rtype.prototype.fields;
15754         delete this.snapshot;
15755         this.sortInfo = meta.sortInfo || this.sortInfo;
15756         this.modified = [];
15757         this.fireEvent('metachange', this, this.reader.meta);
15758     },
15759     
15760     moveIndex : function(data, type)
15761     {
15762         var index = this.indexOf(data);
15763         
15764         var newIndex = index + type;
15765         
15766         this.remove(data);
15767         
15768         this.insert(newIndex, data);
15769         
15770     }
15771 });/*
15772  * Based on:
15773  * Ext JS Library 1.1.1
15774  * Copyright(c) 2006-2007, Ext JS, LLC.
15775  *
15776  * Originally Released Under LGPL - original licence link has changed is not relivant.
15777  *
15778  * Fork - LGPL
15779  * <script type="text/javascript">
15780  */
15781
15782 /**
15783  * @class Roo.data.SimpleStore
15784  * @extends Roo.data.Store
15785  * Small helper class to make creating Stores from Array data easier.
15786  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15787  * @cfg {Array} fields An array of field definition objects, or field name strings.
15788  * @cfg {Object} an existing reader (eg. copied from another store)
15789  * @cfg {Array} data The multi-dimensional array of data
15790  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15791  * @cfg {Roo.data.Reader} reader  [not-required] 
15792  * @constructor
15793  * @param {Object} config
15794  */
15795 Roo.data.SimpleStore = function(config)
15796 {
15797     Roo.data.SimpleStore.superclass.constructor.call(this, {
15798         isLocal : true,
15799         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15800                 id: config.id
15801             },
15802             Roo.data.Record.create(config.fields)
15803         ),
15804         proxy : new Roo.data.MemoryProxy(config.data)
15805     });
15806     this.load();
15807 };
15808 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15809  * Based on:
15810  * Ext JS Library 1.1.1
15811  * Copyright(c) 2006-2007, Ext JS, LLC.
15812  *
15813  * Originally Released Under LGPL - original licence link has changed is not relivant.
15814  *
15815  * Fork - LGPL
15816  * <script type="text/javascript">
15817  */
15818
15819 /**
15820 /**
15821  * @extends Roo.data.Store
15822  * @class Roo.data.JsonStore
15823  * Small helper class to make creating Stores for JSON data easier. <br/>
15824 <pre><code>
15825 var store = new Roo.data.JsonStore({
15826     url: 'get-images.php',
15827     root: 'images',
15828     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15829 });
15830 </code></pre>
15831  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15832  * JsonReader and HttpProxy (unless inline data is provided).</b>
15833  * @cfg {Array} fields An array of field definition objects, or field name strings.
15834  * @constructor
15835  * @param {Object} config
15836  */
15837 Roo.data.JsonStore = function(c){
15838     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15839         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15840         reader: new Roo.data.JsonReader(c, c.fields)
15841     }));
15842 };
15843 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853
15854  
15855 Roo.data.Field = function(config){
15856     if(typeof config == "string"){
15857         config = {name: config};
15858     }
15859     Roo.apply(this, config);
15860     
15861     if(!this.type){
15862         this.type = "auto";
15863     }
15864     
15865     var st = Roo.data.SortTypes;
15866     // named sortTypes are supported, here we look them up
15867     if(typeof this.sortType == "string"){
15868         this.sortType = st[this.sortType];
15869     }
15870     
15871     // set default sortType for strings and dates
15872     if(!this.sortType){
15873         switch(this.type){
15874             case "string":
15875                 this.sortType = st.asUCString;
15876                 break;
15877             case "date":
15878                 this.sortType = st.asDate;
15879                 break;
15880             default:
15881                 this.sortType = st.none;
15882         }
15883     }
15884
15885     // define once
15886     var stripRe = /[\$,%]/g;
15887
15888     // prebuilt conversion function for this field, instead of
15889     // switching every time we're reading a value
15890     if(!this.convert){
15891         var cv, dateFormat = this.dateFormat;
15892         switch(this.type){
15893             case "":
15894             case "auto":
15895             case undefined:
15896                 cv = function(v){ return v; };
15897                 break;
15898             case "string":
15899                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15900                 break;
15901             case "int":
15902                 cv = function(v){
15903                     return v !== undefined && v !== null && v !== '' ?
15904                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15905                     };
15906                 break;
15907             case "float":
15908                 cv = function(v){
15909                     return v !== undefined && v !== null && v !== '' ?
15910                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15911                     };
15912                 break;
15913             case "bool":
15914             case "boolean":
15915                 cv = function(v){ return v === true || v === "true" || v == 1; };
15916                 break;
15917             case "date":
15918                 cv = function(v){
15919                     if(!v){
15920                         return '';
15921                     }
15922                     if(v instanceof Date){
15923                         return v;
15924                     }
15925                     if(dateFormat){
15926                         if(dateFormat == "timestamp"){
15927                             return new Date(v*1000);
15928                         }
15929                         return Date.parseDate(v, dateFormat);
15930                     }
15931                     var parsed = Date.parse(v);
15932                     return parsed ? new Date(parsed) : null;
15933                 };
15934              break;
15935             
15936         }
15937         this.convert = cv;
15938     }
15939 };
15940
15941 Roo.data.Field.prototype = {
15942     dateFormat: null,
15943     defaultValue: "",
15944     mapping: null,
15945     sortType : null,
15946     sortDir : "ASC"
15947 };/*
15948  * Based on:
15949  * Ext JS Library 1.1.1
15950  * Copyright(c) 2006-2007, Ext JS, LLC.
15951  *
15952  * Originally Released Under LGPL - original licence link has changed is not relivant.
15953  *
15954  * Fork - LGPL
15955  * <script type="text/javascript">
15956  */
15957  
15958 // Base class for reading structured data from a data source.  This class is intended to be
15959 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15960
15961 /**
15962  * @class Roo.data.DataReader
15963  * @abstract
15964  * Base class for reading structured data from a data source.  This class is intended to be
15965  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15966  */
15967
15968 Roo.data.DataReader = function(meta, recordType){
15969     
15970     this.meta = meta;
15971     
15972     this.recordType = recordType instanceof Array ? 
15973         Roo.data.Record.create(recordType) : recordType;
15974 };
15975
15976 Roo.data.DataReader.prototype = {
15977     
15978     
15979     readerType : 'Data',
15980      /**
15981      * Create an empty record
15982      * @param {Object} data (optional) - overlay some values
15983      * @return {Roo.data.Record} record created.
15984      */
15985     newRow :  function(d) {
15986         var da =  {};
15987         this.recordType.prototype.fields.each(function(c) {
15988             switch( c.type) {
15989                 case 'int' : da[c.name] = 0; break;
15990                 case 'date' : da[c.name] = new Date(); break;
15991                 case 'float' : da[c.name] = 0.0; break;
15992                 case 'boolean' : da[c.name] = false; break;
15993                 default : da[c.name] = ""; break;
15994             }
15995             
15996         });
15997         return new this.recordType(Roo.apply(da, d));
15998     }
15999     
16000     
16001 };/*
16002  * Based on:
16003  * Ext JS Library 1.1.1
16004  * Copyright(c) 2006-2007, Ext JS, LLC.
16005  *
16006  * Originally Released Under LGPL - original licence link has changed is not relivant.
16007  *
16008  * Fork - LGPL
16009  * <script type="text/javascript">
16010  */
16011
16012 /**
16013  * @class Roo.data.DataProxy
16014  * @extends Roo.util.Observable
16015  * @abstract
16016  * This class is an abstract base class for implementations which provide retrieval of
16017  * unformatted data objects.<br>
16018  * <p>
16019  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16020  * (of the appropriate type which knows how to parse the data object) to provide a block of
16021  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16022  * <p>
16023  * Custom implementations must implement the load method as described in
16024  * {@link Roo.data.HttpProxy#load}.
16025  */
16026 Roo.data.DataProxy = function(){
16027     this.addEvents({
16028         /**
16029          * @event beforeload
16030          * Fires before a network request is made to retrieve a data object.
16031          * @param {Object} This DataProxy object.
16032          * @param {Object} params The params parameter to the load function.
16033          */
16034         beforeload : true,
16035         /**
16036          * @event load
16037          * Fires before the load method's callback is called.
16038          * @param {Object} This DataProxy object.
16039          * @param {Object} o The data object.
16040          * @param {Object} arg The callback argument object passed to the load function.
16041          */
16042         load : true,
16043         /**
16044          * @event loadexception
16045          * Fires if an Exception occurs during data retrieval.
16046          * @param {Object} This DataProxy object.
16047          * @param {Object} o The data object.
16048          * @param {Object} arg The callback argument object passed to the load function.
16049          * @param {Object} e The Exception.
16050          */
16051         loadexception : true
16052     });
16053     Roo.data.DataProxy.superclass.constructor.call(this);
16054 };
16055
16056 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16057
16058     /**
16059      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16060      */
16061 /*
16062  * Based on:
16063  * Ext JS Library 1.1.1
16064  * Copyright(c) 2006-2007, Ext JS, LLC.
16065  *
16066  * Originally Released Under LGPL - original licence link has changed is not relivant.
16067  *
16068  * Fork - LGPL
16069  * <script type="text/javascript">
16070  */
16071 /**
16072  * @class Roo.data.MemoryProxy
16073  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16074  * to the Reader when its load method is called.
16075  * @constructor
16076  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16077  */
16078 Roo.data.MemoryProxy = function(data){
16079     if (data.data) {
16080         data = data.data;
16081     }
16082     Roo.data.MemoryProxy.superclass.constructor.call(this);
16083     this.data = data;
16084 };
16085
16086 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16087     
16088     /**
16089      * Load data from the requested source (in this case an in-memory
16090      * data object passed to the constructor), read the data object into
16091      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16092      * process that block using the passed callback.
16093      * @param {Object} params This parameter is not used by the MemoryProxy class.
16094      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16095      * object into a block of Roo.data.Records.
16096      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16097      * The function must be passed <ul>
16098      * <li>The Record block object</li>
16099      * <li>The "arg" argument from the load function</li>
16100      * <li>A boolean success indicator</li>
16101      * </ul>
16102      * @param {Object} scope The scope in which to call the callback
16103      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16104      */
16105     load : function(params, reader, callback, scope, arg){
16106         params = params || {};
16107         var result;
16108         try {
16109             result = reader.readRecords(params.data ? params.data :this.data);
16110         }catch(e){
16111             this.fireEvent("loadexception", this, arg, null, e);
16112             callback.call(scope, null, arg, false);
16113             return;
16114         }
16115         callback.call(scope, result, arg, true);
16116     },
16117     
16118     // private
16119     update : function(params, records){
16120         
16121     }
16122 });/*
16123  * Based on:
16124  * Ext JS Library 1.1.1
16125  * Copyright(c) 2006-2007, Ext JS, LLC.
16126  *
16127  * Originally Released Under LGPL - original licence link has changed is not relivant.
16128  *
16129  * Fork - LGPL
16130  * <script type="text/javascript">
16131  */
16132 /**
16133  * @class Roo.data.HttpProxy
16134  * @extends Roo.data.DataProxy
16135  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16136  * configured to reference a certain URL.<br><br>
16137  * <p>
16138  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16139  * from which the running page was served.<br><br>
16140  * <p>
16141  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16142  * <p>
16143  * Be aware that to enable the browser to parse an XML document, the server must set
16144  * the Content-Type header in the HTTP response to "text/xml".
16145  * @constructor
16146  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16147  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16148  * will be used to make the request.
16149  */
16150 Roo.data.HttpProxy = function(conn){
16151     Roo.data.HttpProxy.superclass.constructor.call(this);
16152     // is conn a conn config or a real conn?
16153     this.conn = conn;
16154     this.useAjax = !conn || !conn.events;
16155   
16156 };
16157
16158 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16159     // thse are take from connection...
16160     
16161     /**
16162      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16163      */
16164     /**
16165      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16166      * extra parameters to each request made by this object. (defaults to undefined)
16167      */
16168     /**
16169      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16170      *  to each request made by this object. (defaults to undefined)
16171      */
16172     /**
16173      * @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)
16174      */
16175     /**
16176      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16177      */
16178      /**
16179      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16180      * @type Boolean
16181      */
16182   
16183
16184     /**
16185      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16186      * @type Boolean
16187      */
16188     /**
16189      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16190      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16191      * a finer-grained basis than the DataProxy events.
16192      */
16193     getConnection : function(){
16194         return this.useAjax ? Roo.Ajax : this.conn;
16195     },
16196
16197     /**
16198      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16199      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16200      * process that block using the passed callback.
16201      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16202      * for the request to the remote server.
16203      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16204      * object into a block of Roo.data.Records.
16205      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16206      * The function must be passed <ul>
16207      * <li>The Record block object</li>
16208      * <li>The "arg" argument from the load function</li>
16209      * <li>A boolean success indicator</li>
16210      * </ul>
16211      * @param {Object} scope The scope in which to call the callback
16212      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16213      */
16214     load : function(params, reader, callback, scope, arg){
16215         if(this.fireEvent("beforeload", this, params) !== false){
16216             var  o = {
16217                 params : params || {},
16218                 request: {
16219                     callback : callback,
16220                     scope : scope,
16221                     arg : arg
16222                 },
16223                 reader: reader,
16224                 callback : this.loadResponse,
16225                 scope: this
16226             };
16227             if(this.useAjax){
16228                 Roo.applyIf(o, this.conn);
16229                 if(this.activeRequest){
16230                     Roo.Ajax.abort(this.activeRequest);
16231                 }
16232                 this.activeRequest = Roo.Ajax.request(o);
16233             }else{
16234                 this.conn.request(o);
16235             }
16236         }else{
16237             callback.call(scope||this, null, arg, false);
16238         }
16239     },
16240
16241     // private
16242     loadResponse : function(o, success, response){
16243         delete this.activeRequest;
16244         if(!success){
16245             this.fireEvent("loadexception", this, o, response);
16246             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16247             return;
16248         }
16249         var result;
16250         try {
16251             result = o.reader.read(response);
16252         }catch(e){
16253             this.fireEvent("loadexception", this, o, response, e);
16254             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16255             return;
16256         }
16257         
16258         this.fireEvent("load", this, o, o.request.arg);
16259         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16260     },
16261
16262     // private
16263     update : function(dataSet){
16264
16265     },
16266
16267     // private
16268     updateResponse : function(dataSet){
16269
16270     }
16271 });/*
16272  * Based on:
16273  * Ext JS Library 1.1.1
16274  * Copyright(c) 2006-2007, Ext JS, LLC.
16275  *
16276  * Originally Released Under LGPL - original licence link has changed is not relivant.
16277  *
16278  * Fork - LGPL
16279  * <script type="text/javascript">
16280  */
16281
16282 /**
16283  * @class Roo.data.ScriptTagProxy
16284  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16285  * other than the originating domain of the running page.<br><br>
16286  * <p>
16287  * <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
16288  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16289  * <p>
16290  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16291  * source code that is used as the source inside a &lt;script> tag.<br><br>
16292  * <p>
16293  * In order for the browser to process the returned data, the server must wrap the data object
16294  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16295  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16296  * depending on whether the callback name was passed:
16297  * <p>
16298  * <pre><code>
16299 boolean scriptTag = false;
16300 String cb = request.getParameter("callback");
16301 if (cb != null) {
16302     scriptTag = true;
16303     response.setContentType("text/javascript");
16304 } else {
16305     response.setContentType("application/x-json");
16306 }
16307 Writer out = response.getWriter();
16308 if (scriptTag) {
16309     out.write(cb + "(");
16310 }
16311 out.print(dataBlock.toJsonString());
16312 if (scriptTag) {
16313     out.write(");");
16314 }
16315 </pre></code>
16316  *
16317  * @constructor
16318  * @param {Object} config A configuration object.
16319  */
16320 Roo.data.ScriptTagProxy = function(config){
16321     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16322     Roo.apply(this, config);
16323     this.head = document.getElementsByTagName("head")[0];
16324 };
16325
16326 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16327
16328 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16329     /**
16330      * @cfg {String} url The URL from which to request the data object.
16331      */
16332     /**
16333      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16334      */
16335     timeout : 30000,
16336     /**
16337      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16338      * the server the name of the callback function set up by the load call to process the returned data object.
16339      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16340      * javascript output which calls this named function passing the data object as its only parameter.
16341      */
16342     callbackParam : "callback",
16343     /**
16344      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16345      * name to the request.
16346      */
16347     nocache : true,
16348
16349     /**
16350      * Load data from the configured URL, read the data object into
16351      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16352      * process that block using the passed callback.
16353      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16354      * for the request to the remote server.
16355      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16356      * object into a block of Roo.data.Records.
16357      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16358      * The function must be passed <ul>
16359      * <li>The Record block object</li>
16360      * <li>The "arg" argument from the load function</li>
16361      * <li>A boolean success indicator</li>
16362      * </ul>
16363      * @param {Object} scope The scope in which to call the callback
16364      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16365      */
16366     load : function(params, reader, callback, scope, arg){
16367         if(this.fireEvent("beforeload", this, params) !== false){
16368
16369             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16370
16371             var url = this.url;
16372             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16373             if(this.nocache){
16374                 url += "&_dc=" + (new Date().getTime());
16375             }
16376             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16377             var trans = {
16378                 id : transId,
16379                 cb : "stcCallback"+transId,
16380                 scriptId : "stcScript"+transId,
16381                 params : params,
16382                 arg : arg,
16383                 url : url,
16384                 callback : callback,
16385                 scope : scope,
16386                 reader : reader
16387             };
16388             var conn = this;
16389
16390             window[trans.cb] = function(o){
16391                 conn.handleResponse(o, trans);
16392             };
16393
16394             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16395
16396             if(this.autoAbort !== false){
16397                 this.abort();
16398             }
16399
16400             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16401
16402             var script = document.createElement("script");
16403             script.setAttribute("src", url);
16404             script.setAttribute("type", "text/javascript");
16405             script.setAttribute("id", trans.scriptId);
16406             this.head.appendChild(script);
16407
16408             this.trans = trans;
16409         }else{
16410             callback.call(scope||this, null, arg, false);
16411         }
16412     },
16413
16414     // private
16415     isLoading : function(){
16416         return this.trans ? true : false;
16417     },
16418
16419     /**
16420      * Abort the current server request.
16421      */
16422     abort : function(){
16423         if(this.isLoading()){
16424             this.destroyTrans(this.trans);
16425         }
16426     },
16427
16428     // private
16429     destroyTrans : function(trans, isLoaded){
16430         this.head.removeChild(document.getElementById(trans.scriptId));
16431         clearTimeout(trans.timeoutId);
16432         if(isLoaded){
16433             window[trans.cb] = undefined;
16434             try{
16435                 delete window[trans.cb];
16436             }catch(e){}
16437         }else{
16438             // if hasn't been loaded, wait for load to remove it to prevent script error
16439             window[trans.cb] = function(){
16440                 window[trans.cb] = undefined;
16441                 try{
16442                     delete window[trans.cb];
16443                 }catch(e){}
16444             };
16445         }
16446     },
16447
16448     // private
16449     handleResponse : function(o, trans){
16450         this.trans = false;
16451         this.destroyTrans(trans, true);
16452         var result;
16453         try {
16454             result = trans.reader.readRecords(o);
16455         }catch(e){
16456             this.fireEvent("loadexception", this, o, trans.arg, e);
16457             trans.callback.call(trans.scope||window, null, trans.arg, false);
16458             return;
16459         }
16460         this.fireEvent("load", this, o, trans.arg);
16461         trans.callback.call(trans.scope||window, result, trans.arg, true);
16462     },
16463
16464     // private
16465     handleFailure : function(trans){
16466         this.trans = false;
16467         this.destroyTrans(trans, false);
16468         this.fireEvent("loadexception", this, null, trans.arg);
16469         trans.callback.call(trans.scope||window, null, trans.arg, false);
16470     }
16471 });/*
16472  * Based on:
16473  * Ext JS Library 1.1.1
16474  * Copyright(c) 2006-2007, Ext JS, LLC.
16475  *
16476  * Originally Released Under LGPL - original licence link has changed is not relivant.
16477  *
16478  * Fork - LGPL
16479  * <script type="text/javascript">
16480  */
16481
16482 /**
16483  * @class Roo.data.JsonReader
16484  * @extends Roo.data.DataReader
16485  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16486  * based on mappings in a provided Roo.data.Record constructor.
16487  * 
16488  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16489  * in the reply previously. 
16490  * 
16491  * <p>
16492  * Example code:
16493  * <pre><code>
16494 var RecordDef = Roo.data.Record.create([
16495     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16496     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16497 ]);
16498 var myReader = new Roo.data.JsonReader({
16499     totalProperty: "results",    // The property which contains the total dataset size (optional)
16500     root: "rows",                // The property which contains an Array of row objects
16501     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16502 }, RecordDef);
16503 </code></pre>
16504  * <p>
16505  * This would consume a JSON file like this:
16506  * <pre><code>
16507 { 'results': 2, 'rows': [
16508     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16509     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16510 }
16511 </code></pre>
16512  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16513  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16514  * paged from the remote server.
16515  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16516  * @cfg {String} root name of the property which contains the Array of row objects.
16517  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16518  * @cfg {Array} fields Array of field definition objects
16519  * @constructor
16520  * Create a new JsonReader
16521  * @param {Object} meta Metadata configuration options
16522  * @param {Object} recordType Either an Array of field definition objects,
16523  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16524  */
16525 Roo.data.JsonReader = function(meta, recordType){
16526     
16527     meta = meta || {};
16528     // set some defaults:
16529     Roo.applyIf(meta, {
16530         totalProperty: 'total',
16531         successProperty : 'success',
16532         root : 'data',
16533         id : 'id'
16534     });
16535     
16536     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16537 };
16538 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16539     
16540     readerType : 'Json',
16541     
16542     /**
16543      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16544      * Used by Store query builder to append _requestMeta to params.
16545      * 
16546      */
16547     metaFromRemote : false,
16548     /**
16549      * This method is only used by a DataProxy which has retrieved data from a remote server.
16550      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16551      * @return {Object} data A data block which is used by an Roo.data.Store object as
16552      * a cache of Roo.data.Records.
16553      */
16554     read : function(response){
16555         var json = response.responseText;
16556        
16557         var o = /* eval:var:o */ eval("("+json+")");
16558         if(!o) {
16559             throw {message: "JsonReader.read: Json object not found"};
16560         }
16561         
16562         if(o.metaData){
16563             
16564             delete this.ef;
16565             this.metaFromRemote = true;
16566             this.meta = o.metaData;
16567             this.recordType = Roo.data.Record.create(o.metaData.fields);
16568             this.onMetaChange(this.meta, this.recordType, o);
16569         }
16570         return this.readRecords(o);
16571     },
16572
16573     // private function a store will implement
16574     onMetaChange : function(meta, recordType, o){
16575
16576     },
16577
16578     /**
16579          * @ignore
16580          */
16581     simpleAccess: function(obj, subsc) {
16582         return obj[subsc];
16583     },
16584
16585         /**
16586          * @ignore
16587          */
16588     getJsonAccessor: function(){
16589         var re = /[\[\.]/;
16590         return function(expr) {
16591             try {
16592                 return(re.test(expr))
16593                     ? new Function("obj", "return obj." + expr)
16594                     : function(obj){
16595                         return obj[expr];
16596                     };
16597             } catch(e){}
16598             return Roo.emptyFn;
16599         };
16600     }(),
16601
16602     /**
16603      * Create a data block containing Roo.data.Records from an XML document.
16604      * @param {Object} o An object which contains an Array of row objects in the property specified
16605      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16606      * which contains the total size of the dataset.
16607      * @return {Object} data A data block which is used by an Roo.data.Store object as
16608      * a cache of Roo.data.Records.
16609      */
16610     readRecords : function(o){
16611         /**
16612          * After any data loads, the raw JSON data is available for further custom processing.
16613          * @type Object
16614          */
16615         this.o = o;
16616         var s = this.meta, Record = this.recordType,
16617             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16618
16619 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16620         if (!this.ef) {
16621             if(s.totalProperty) {
16622                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16623                 }
16624                 if(s.successProperty) {
16625                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16626                 }
16627                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16628                 if (s.id) {
16629                         var g = this.getJsonAccessor(s.id);
16630                         this.getId = function(rec) {
16631                                 var r = g(rec);  
16632                                 return (r === undefined || r === "") ? null : r;
16633                         };
16634                 } else {
16635                         this.getId = function(){return null;};
16636                 }
16637             this.ef = [];
16638             for(var jj = 0; jj < fl; jj++){
16639                 f = fi[jj];
16640                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16641                 this.ef[jj] = this.getJsonAccessor(map);
16642             }
16643         }
16644
16645         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16646         if(s.totalProperty){
16647             var vt = parseInt(this.getTotal(o), 10);
16648             if(!isNaN(vt)){
16649                 totalRecords = vt;
16650             }
16651         }
16652         if(s.successProperty){
16653             var vs = this.getSuccess(o);
16654             if(vs === false || vs === 'false'){
16655                 success = false;
16656             }
16657         }
16658         var records = [];
16659         for(var i = 0; i < c; i++){
16660                 var n = root[i];
16661             var values = {};
16662             var id = this.getId(n);
16663             for(var j = 0; j < fl; j++){
16664                 f = fi[j];
16665             var v = this.ef[j](n);
16666             if (!f.convert) {
16667                 Roo.log('missing convert for ' + f.name);
16668                 Roo.log(f);
16669                 continue;
16670             }
16671             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16672             }
16673             var record = new Record(values, id);
16674             record.json = n;
16675             records[i] = record;
16676         }
16677         return {
16678             raw : o,
16679             success : success,
16680             records : records,
16681             totalRecords : totalRecords
16682         };
16683     },
16684     // used when loading children.. @see loadDataFromChildren
16685     toLoadData: function(rec)
16686     {
16687         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16688         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16689         return { data : data, total : data.length };
16690         
16691     }
16692 });/*
16693  * Based on:
16694  * Ext JS Library 1.1.1
16695  * Copyright(c) 2006-2007, Ext JS, LLC.
16696  *
16697  * Originally Released Under LGPL - original licence link has changed is not relivant.
16698  *
16699  * Fork - LGPL
16700  * <script type="text/javascript">
16701  */
16702
16703 /**
16704  * @class Roo.data.ArrayReader
16705  * @extends Roo.data.DataReader
16706  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16707  * Each element of that Array represents a row of data fields. The
16708  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16709  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16710  * <p>
16711  * Example code:.
16712  * <pre><code>
16713 var RecordDef = Roo.data.Record.create([
16714     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16715     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16716 ]);
16717 var myReader = new Roo.data.ArrayReader({
16718     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16719 }, RecordDef);
16720 </code></pre>
16721  * <p>
16722  * This would consume an Array like this:
16723  * <pre><code>
16724 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16725   </code></pre>
16726  
16727  * @constructor
16728  * Create a new JsonReader
16729  * @param {Object} meta Metadata configuration options.
16730  * @param {Object|Array} recordType Either an Array of field definition objects
16731  * 
16732  * @cfg {Array} fields Array of field definition objects
16733  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16734  * as specified to {@link Roo.data.Record#create},
16735  * or an {@link Roo.data.Record} object
16736  *
16737  * 
16738  * created using {@link Roo.data.Record#create}.
16739  */
16740 Roo.data.ArrayReader = function(meta, recordType)
16741 {    
16742     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16743 };
16744
16745 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16746     
16747       /**
16748      * Create a data block containing Roo.data.Records from an XML document.
16749      * @param {Object} o An Array of row objects which represents the dataset.
16750      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16751      * a cache of Roo.data.Records.
16752      */
16753     readRecords : function(o)
16754     {
16755         var sid = this.meta ? this.meta.id : null;
16756         var recordType = this.recordType, fields = recordType.prototype.fields;
16757         var records = [];
16758         var root = o;
16759         for(var i = 0; i < root.length; i++){
16760             var n = root[i];
16761             var values = {};
16762             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16763             for(var j = 0, jlen = fields.length; j < jlen; j++){
16764                 var f = fields.items[j];
16765                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16766                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16767                 v = f.convert(v);
16768                 values[f.name] = v;
16769             }
16770             var record = new recordType(values, id);
16771             record.json = n;
16772             records[records.length] = record;
16773         }
16774         return {
16775             records : records,
16776             totalRecords : records.length
16777         };
16778     },
16779     // used when loading children.. @see loadDataFromChildren
16780     toLoadData: function(rec)
16781     {
16782         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16783         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16784         
16785     }
16786     
16787     
16788 });/*
16789  * - LGPL
16790  * * 
16791  */
16792
16793 /**
16794  * @class Roo.bootstrap.ComboBox
16795  * @extends Roo.bootstrap.TriggerField
16796  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16797  * @cfg {Boolean} append (true|false) default false
16798  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16799  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16800  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16801  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16802  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16803  * @cfg {Boolean} animate default true
16804  * @cfg {Boolean} emptyResultText only for touch device
16805  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16806  * @cfg {String} emptyTitle default ''
16807  * @cfg {Number} width fixed with? experimental
16808  * @constructor
16809  * Create a new ComboBox.
16810  * @param {Object} config Configuration options
16811  */
16812 Roo.bootstrap.ComboBox = function(config){
16813     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16814     this.addEvents({
16815         /**
16816          * @event expand
16817          * Fires when the dropdown list is expanded
16818         * @param {Roo.bootstrap.ComboBox} combo This combo box
16819         */
16820         'expand' : true,
16821         /**
16822          * @event collapse
16823          * Fires when the dropdown list is collapsed
16824         * @param {Roo.bootstrap.ComboBox} combo This combo box
16825         */
16826         'collapse' : true,
16827         /**
16828          * @event beforeselect
16829          * Fires before a list item is selected. Return false to cancel the selection.
16830         * @param {Roo.bootstrap.ComboBox} combo This combo box
16831         * @param {Roo.data.Record} record The data record returned from the underlying store
16832         * @param {Number} index The index of the selected item in the dropdown list
16833         */
16834         'beforeselect' : true,
16835         /**
16836          * @event select
16837          * Fires when a list item is selected
16838         * @param {Roo.bootstrap.ComboBox} combo This combo box
16839         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16840         * @param {Number} index The index of the selected item in the dropdown list
16841         */
16842         'select' : true,
16843         /**
16844          * @event beforequery
16845          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16846          * The event object passed has these properties:
16847         * @param {Roo.bootstrap.ComboBox} combo This combo box
16848         * @param {String} query The query
16849         * @param {Boolean} forceAll true to force "all" query
16850         * @param {Boolean} cancel true to cancel the query
16851         * @param {Object} e The query event object
16852         */
16853         'beforequery': true,
16854          /**
16855          * @event add
16856          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16857         * @param {Roo.bootstrap.ComboBox} combo This combo box
16858         */
16859         'add' : true,
16860         /**
16861          * @event edit
16862          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16863         * @param {Roo.bootstrap.ComboBox} combo This combo box
16864         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16865         */
16866         'edit' : true,
16867         /**
16868          * @event remove
16869          * Fires when the remove value from the combobox array
16870         * @param {Roo.bootstrap.ComboBox} combo This combo box
16871         */
16872         'remove' : true,
16873         /**
16874          * @event afterremove
16875          * Fires when the remove value from the combobox array
16876         * @param {Roo.bootstrap.ComboBox} combo This combo box
16877         */
16878         'afterremove' : true,
16879         /**
16880          * @event specialfilter
16881          * Fires when specialfilter
16882             * @param {Roo.bootstrap.ComboBox} combo This combo box
16883             */
16884         'specialfilter' : true,
16885         /**
16886          * @event tick
16887          * Fires when tick the element
16888             * @param {Roo.bootstrap.ComboBox} combo This combo box
16889             */
16890         'tick' : true,
16891         /**
16892          * @event touchviewdisplay
16893          * Fires when touch view require special display (default is using displayField)
16894             * @param {Roo.bootstrap.ComboBox} combo This combo box
16895             * @param {Object} cfg set html .
16896             */
16897         'touchviewdisplay' : true
16898         
16899     });
16900     
16901     this.item = [];
16902     this.tickItems = [];
16903     
16904     this.selectedIndex = -1;
16905     if(this.mode == 'local'){
16906         if(config.queryDelay === undefined){
16907             this.queryDelay = 10;
16908         }
16909         if(config.minChars === undefined){
16910             this.minChars = 0;
16911         }
16912     }
16913 };
16914
16915 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16916      
16917     /**
16918      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16919      * rendering into an Roo.Editor, defaults to false)
16920      */
16921     /**
16922      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16923      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16924      */
16925     /**
16926      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16927      */
16928     /**
16929      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16930      * the dropdown list (defaults to undefined, with no header element)
16931      */
16932
16933      /**
16934      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16935      */
16936      
16937      /**
16938      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16939      */
16940     listWidth: undefined,
16941     /**
16942      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16943      * mode = 'remote' or 'text' if mode = 'local')
16944      */
16945     displayField: undefined,
16946     
16947     /**
16948      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16949      * mode = 'remote' or 'value' if mode = 'local'). 
16950      * Note: use of a valueField requires the user make a selection
16951      * in order for a value to be mapped.
16952      */
16953     valueField: undefined,
16954     /**
16955      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16956      */
16957     modalTitle : '',
16958     
16959     /**
16960      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16961      * field's data value (defaults to the underlying DOM element's name)
16962      */
16963     hiddenName: undefined,
16964     /**
16965      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16966      */
16967     listClass: '',
16968     /**
16969      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16970      */
16971     selectedClass: 'active',
16972     
16973     /**
16974      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16975      */
16976     shadow:'sides',
16977     /**
16978      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16979      * anchor positions (defaults to 'tl-bl')
16980      */
16981     listAlign: 'tl-bl?',
16982     /**
16983      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16984      */
16985     maxHeight: 300,
16986     /**
16987      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16988      * query specified by the allQuery config option (defaults to 'query')
16989      */
16990     triggerAction: 'query',
16991     /**
16992      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16993      * (defaults to 4, does not apply if editable = false)
16994      */
16995     minChars : 4,
16996     /**
16997      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16998      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16999      */
17000     typeAhead: false,
17001     /**
17002      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17003      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17004      */
17005     queryDelay: 500,
17006     /**
17007      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17008      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17009      */
17010     pageSize: 0,
17011     /**
17012      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17013      * when editable = true (defaults to false)
17014      */
17015     selectOnFocus:false,
17016     /**
17017      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17018      */
17019     queryParam: 'query',
17020     /**
17021      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17022      * when mode = 'remote' (defaults to 'Loading...')
17023      */
17024     loadingText: 'Loading...',
17025     /**
17026      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17027      */
17028     resizable: false,
17029     /**
17030      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17031      */
17032     handleHeight : 8,
17033     /**
17034      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17035      * traditional select (defaults to true)
17036      */
17037     editable: true,
17038     /**
17039      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17040      */
17041     allQuery: '',
17042     /**
17043      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17044      */
17045     mode: 'remote',
17046     /**
17047      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17048      * listWidth has a higher value)
17049      */
17050     minListWidth : 70,
17051     /**
17052      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17053      * allow the user to set arbitrary text into the field (defaults to false)
17054      */
17055     forceSelection:false,
17056     /**
17057      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17058      * if typeAhead = true (defaults to 250)
17059      */
17060     typeAheadDelay : 250,
17061     /**
17062      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17063      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17064      */
17065     valueNotFoundText : undefined,
17066     /**
17067      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17068      */
17069     blockFocus : false,
17070     
17071     /**
17072      * @cfg {Boolean} disableClear Disable showing of clear button.
17073      */
17074     disableClear : false,
17075     /**
17076      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17077      */
17078     alwaysQuery : false,
17079     
17080     /**
17081      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17082      */
17083     multiple : false,
17084     
17085     /**
17086      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17087      */
17088     invalidClass : "has-warning",
17089     
17090     /**
17091      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17092      */
17093     validClass : "has-success",
17094     
17095     /**
17096      * @cfg {Boolean} specialFilter (true|false) special filter default false
17097      */
17098     specialFilter : false,
17099     
17100     /**
17101      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17102      */
17103     mobileTouchView : true,
17104     
17105     /**
17106      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17107      */
17108     useNativeIOS : false,
17109     
17110     /**
17111      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17112      */
17113     mobile_restrict_height : false,
17114     
17115     ios_options : false,
17116     
17117     //private
17118     addicon : false,
17119     editicon: false,
17120     
17121     page: 0,
17122     hasQuery: false,
17123     append: false,
17124     loadNext: false,
17125     autoFocus : true,
17126     tickable : false,
17127     btnPosition : 'right',
17128     triggerList : true,
17129     showToggleBtn : true,
17130     animate : true,
17131     emptyResultText: 'Empty',
17132     triggerText : 'Select',
17133     emptyTitle : '',
17134     width : false,
17135     
17136     // element that contains real text value.. (when hidden is used..)
17137     
17138     getAutoCreate : function()
17139     {   
17140         var cfg = false;
17141         //render
17142         /*
17143          * Render classic select for iso
17144          */
17145         
17146         if(Roo.isIOS && this.useNativeIOS){
17147             cfg = this.getAutoCreateNativeIOS();
17148             return cfg;
17149         }
17150         
17151         /*
17152          * Touch Devices
17153          */
17154         
17155         if(Roo.isTouch && this.mobileTouchView){
17156             cfg = this.getAutoCreateTouchView();
17157             return cfg;;
17158         }
17159         
17160         /*
17161          *  Normal ComboBox
17162          */
17163         if(!this.tickable){
17164             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
17165             return cfg;
17166         }
17167         
17168         /*
17169          *  ComboBox with tickable selections
17170          */
17171              
17172         var align = this.labelAlign || this.parentLabelAlign();
17173         
17174         cfg = {
17175             cls : 'form-group roo-combobox-tickable' //input-group
17176         };
17177         
17178         var btn_text_select = '';
17179         var btn_text_done = '';
17180         var btn_text_cancel = '';
17181         
17182         if (this.btn_text_show) {
17183             btn_text_select = 'Select';
17184             btn_text_done = 'Done';
17185             btn_text_cancel = 'Cancel'; 
17186         }
17187         
17188         var buttons = {
17189             tag : 'div',
17190             cls : 'tickable-buttons',
17191             cn : [
17192                 {
17193                     tag : 'button',
17194                     type : 'button',
17195                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17196                     //html : this.triggerText
17197                     html: btn_text_select
17198                 },
17199                 {
17200                     tag : 'button',
17201                     type : 'button',
17202                     name : 'ok',
17203                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17204                     //html : 'Done'
17205                     html: btn_text_done
17206                 },
17207                 {
17208                     tag : 'button',
17209                     type : 'button',
17210                     name : 'cancel',
17211                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17212                     //html : 'Cancel'
17213                     html: btn_text_cancel
17214                 }
17215             ]
17216         };
17217         
17218         if(this.editable){
17219             buttons.cn.unshift({
17220                 tag: 'input',
17221                 cls: 'roo-select2-search-field-input'
17222             });
17223         }
17224         
17225         var _this = this;
17226         
17227         Roo.each(buttons.cn, function(c){
17228             if (_this.size) {
17229                 c.cls += ' btn-' + _this.size;
17230             }
17231
17232             if (_this.disabled) {
17233                 c.disabled = true;
17234             }
17235         });
17236         
17237         var box = {
17238             tag: 'div',
17239             style : 'display: contents',
17240             cn: [
17241                 {
17242                     tag: 'input',
17243                     type : 'hidden',
17244                     cls: 'form-hidden-field'
17245                 },
17246                 {
17247                     tag: 'ul',
17248                     cls: 'roo-select2-choices',
17249                     cn:[
17250                         {
17251                             tag: 'li',
17252                             cls: 'roo-select2-search-field',
17253                             cn: [
17254                                 buttons
17255                             ]
17256                         }
17257                     ]
17258                 }
17259             ]
17260         };
17261         
17262         var combobox = {
17263             cls: 'roo-select2-container input-group roo-select2-container-multi',
17264             cn: [
17265                 
17266                 box
17267 //                {
17268 //                    tag: 'ul',
17269 //                    cls: 'typeahead typeahead-long dropdown-menu',
17270 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17271 //                }
17272             ]
17273         };
17274         
17275         if(this.hasFeedback && !this.allowBlank){
17276             
17277             var feedback = {
17278                 tag: 'span',
17279                 cls: 'glyphicon form-control-feedback'
17280             };
17281
17282             combobox.cn.push(feedback);
17283         }
17284         
17285         
17286         
17287         var indicator = {
17288             tag : 'i',
17289             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17290             tooltip : 'This field is required'
17291         };
17292         if (Roo.bootstrap.version == 4) {
17293             indicator = {
17294                 tag : 'i',
17295                 style : 'display:none'
17296             };
17297         }
17298         if (align ==='left' && this.fieldLabel.length) {
17299             
17300             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17301             
17302             cfg.cn = [
17303                 indicator,
17304                 {
17305                     tag: 'label',
17306                     'for' :  id,
17307                     cls : 'control-label col-form-label',
17308                     html : this.fieldLabel
17309
17310                 },
17311                 {
17312                     cls : "", 
17313                     cn: [
17314                         combobox
17315                     ]
17316                 }
17317
17318             ];
17319             
17320             var labelCfg = cfg.cn[1];
17321             var contentCfg = cfg.cn[2];
17322             
17323
17324             if(this.indicatorpos == 'right'){
17325                 
17326                 cfg.cn = [
17327                     {
17328                         tag: 'label',
17329                         'for' :  id,
17330                         cls : 'control-label col-form-label',
17331                         cn : [
17332                             {
17333                                 tag : 'span',
17334                                 html : this.fieldLabel
17335                             },
17336                             indicator
17337                         ]
17338                     },
17339                     {
17340                         cls : "",
17341                         cn: [
17342                             combobox
17343                         ]
17344                     }
17345
17346                 ];
17347                 
17348                 
17349                 
17350                 labelCfg = cfg.cn[0];
17351                 contentCfg = cfg.cn[1];
17352             
17353             }
17354             
17355             if(this.labelWidth > 12){
17356                 labelCfg.style = "width: " + this.labelWidth + 'px';
17357             }
17358             if(this.width * 1 > 0){
17359                 contentCfg.style = "width: " + this.width + 'px';
17360             }
17361             if(this.labelWidth < 13 && this.labelmd == 0){
17362                 this.labelmd = this.labelWidth;
17363             }
17364             
17365             if(this.labellg > 0){
17366                 labelCfg.cls += ' col-lg-' + this.labellg;
17367                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17368             }
17369             
17370             if(this.labelmd > 0){
17371                 labelCfg.cls += ' col-md-' + this.labelmd;
17372                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17373             }
17374             
17375             if(this.labelsm > 0){
17376                 labelCfg.cls += ' col-sm-' + this.labelsm;
17377                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17378             }
17379             
17380             if(this.labelxs > 0){
17381                 labelCfg.cls += ' col-xs-' + this.labelxs;
17382                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17383             }
17384                 
17385                 
17386         } else if ( this.fieldLabel.length) {
17387 //                Roo.log(" label");
17388                  cfg.cn = [
17389                    indicator,
17390                     {
17391                         tag: 'label',
17392                         //cls : 'input-group-addon',
17393                         html : this.fieldLabel
17394                     },
17395                     combobox
17396                 ];
17397                 
17398                 if(this.indicatorpos == 'right'){
17399                     cfg.cn = [
17400                         {
17401                             tag: 'label',
17402                             //cls : 'input-group-addon',
17403                             html : this.fieldLabel
17404                         },
17405                         indicator,
17406                         combobox
17407                     ];
17408                     
17409                 }
17410
17411         } else {
17412             
17413 //                Roo.log(" no label && no align");
17414                 cfg = combobox
17415                      
17416                 
17417         }
17418          
17419         var settings=this;
17420         ['xs','sm','md','lg'].map(function(size){
17421             if (settings[size]) {
17422                 cfg.cls += ' col-' + size + '-' + settings[size];
17423             }
17424         });
17425         
17426         return cfg;
17427         
17428     },
17429     
17430     _initEventsCalled : false,
17431     
17432     // private
17433     initEvents: function()
17434     {   
17435         if (this._initEventsCalled) { // as we call render... prevent looping...
17436             return;
17437         }
17438         this._initEventsCalled = true;
17439         
17440         if (!this.store) {
17441             throw "can not find store for combo";
17442         }
17443         
17444         this.indicator = this.indicatorEl();
17445         
17446         this.store = Roo.factory(this.store, Roo.data);
17447         this.store.parent = this;
17448         
17449         // if we are building from html. then this element is so complex, that we can not really
17450         // use the rendered HTML.
17451         // so we have to trash and replace the previous code.
17452         if (Roo.XComponent.build_from_html) {
17453             // remove this element....
17454             var e = this.el.dom, k=0;
17455             while (e ) { e = e.previousSibling;  ++k;}
17456
17457             this.el.remove();
17458             
17459             this.el=false;
17460             this.rendered = false;
17461             
17462             this.render(this.parent().getChildContainer(true), k);
17463         }
17464         
17465         if(Roo.isIOS && this.useNativeIOS){
17466             this.initIOSView();
17467             return;
17468         }
17469         
17470         /*
17471          * Touch Devices
17472          */
17473         
17474         if(Roo.isTouch && this.mobileTouchView){
17475             this.initTouchView();
17476             return;
17477         }
17478         
17479         if(this.tickable){
17480             this.initTickableEvents();
17481             return;
17482         }
17483         
17484         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17485         
17486         if(this.hiddenName){
17487             
17488             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17489             
17490             this.hiddenField.dom.value =
17491                 this.hiddenValue !== undefined ? this.hiddenValue :
17492                 this.value !== undefined ? this.value : '';
17493
17494             // prevent input submission
17495             this.el.dom.removeAttribute('name');
17496             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17497              
17498              
17499         }
17500         //if(Roo.isGecko){
17501         //    this.el.dom.setAttribute('autocomplete', 'off');
17502         //}
17503         
17504         var cls = 'x-combo-list';
17505         
17506         //this.list = new Roo.Layer({
17507         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17508         //});
17509         
17510         var _this = this;
17511         
17512         (function(){
17513             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17514             _this.list.setWidth(lw);
17515         }).defer(100);
17516         
17517         this.list.on('mouseover', this.onViewOver, this);
17518         this.list.on('mousemove', this.onViewMove, this);
17519         this.list.on('scroll', this.onViewScroll, this);
17520         
17521         /*
17522         this.list.swallowEvent('mousewheel');
17523         this.assetHeight = 0;
17524
17525         if(this.title){
17526             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17527             this.assetHeight += this.header.getHeight();
17528         }
17529
17530         this.innerList = this.list.createChild({cls:cls+'-inner'});
17531         this.innerList.on('mouseover', this.onViewOver, this);
17532         this.innerList.on('mousemove', this.onViewMove, this);
17533         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17534         
17535         if(this.allowBlank && !this.pageSize && !this.disableClear){
17536             this.footer = this.list.createChild({cls:cls+'-ft'});
17537             this.pageTb = new Roo.Toolbar(this.footer);
17538            
17539         }
17540         if(this.pageSize){
17541             this.footer = this.list.createChild({cls:cls+'-ft'});
17542             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17543                     {pageSize: this.pageSize});
17544             
17545         }
17546         
17547         if (this.pageTb && this.allowBlank && !this.disableClear) {
17548             var _this = this;
17549             this.pageTb.add(new Roo.Toolbar.Fill(), {
17550                 cls: 'x-btn-icon x-btn-clear',
17551                 text: '&#160;',
17552                 handler: function()
17553                 {
17554                     _this.collapse();
17555                     _this.clearValue();
17556                     _this.onSelect(false, -1);
17557                 }
17558             });
17559         }
17560         if (this.footer) {
17561             this.assetHeight += this.footer.getHeight();
17562         }
17563         */
17564             
17565         if(!this.tpl){
17566             this.tpl = Roo.bootstrap.version == 4 ?
17567                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17568                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17569         }
17570
17571         this.view = new Roo.View(this.list, this.tpl, {
17572             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17573         });
17574         //this.view.wrapEl.setDisplayed(false);
17575         this.view.on('click', this.onViewClick, this);
17576         
17577         
17578         this.store.on('beforeload', this.onBeforeLoad, this);
17579         this.store.on('load', this.onLoad, this);
17580         this.store.on('loadexception', this.onLoadException, this);
17581         /*
17582         if(this.resizable){
17583             this.resizer = new Roo.Resizable(this.list,  {
17584                pinned:true, handles:'se'
17585             });
17586             this.resizer.on('resize', function(r, w, h){
17587                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17588                 this.listWidth = w;
17589                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17590                 this.restrictHeight();
17591             }, this);
17592             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17593         }
17594         */
17595         if(!this.editable){
17596             this.editable = true;
17597             this.setEditable(false);
17598         }
17599         
17600         /*
17601         
17602         if (typeof(this.events.add.listeners) != 'undefined') {
17603             
17604             this.addicon = this.wrap.createChild(
17605                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17606        
17607             this.addicon.on('click', function(e) {
17608                 this.fireEvent('add', this);
17609             }, this);
17610         }
17611         if (typeof(this.events.edit.listeners) != 'undefined') {
17612             
17613             this.editicon = this.wrap.createChild(
17614                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17615             if (this.addicon) {
17616                 this.editicon.setStyle('margin-left', '40px');
17617             }
17618             this.editicon.on('click', function(e) {
17619                 
17620                 // we fire even  if inothing is selected..
17621                 this.fireEvent('edit', this, this.lastData );
17622                 
17623             }, this);
17624         }
17625         */
17626         
17627         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17628             "up" : function(e){
17629                 this.inKeyMode = true;
17630                 this.selectPrev();
17631             },
17632
17633             "down" : function(e){
17634                 if(!this.isExpanded()){
17635                     this.onTriggerClick();
17636                 }else{
17637                     this.inKeyMode = true;
17638                     this.selectNext();
17639                 }
17640             },
17641
17642             "enter" : function(e){
17643 //                this.onViewClick();
17644                 //return true;
17645                 this.collapse();
17646                 
17647                 if(this.fireEvent("specialkey", this, e)){
17648                     this.onViewClick(false);
17649                 }
17650                 
17651                 return true;
17652             },
17653
17654             "esc" : function(e){
17655                 this.collapse();
17656             },
17657
17658             "tab" : function(e){
17659                 this.collapse();
17660                 
17661                 if(this.fireEvent("specialkey", this, e)){
17662                     this.onViewClick(false);
17663                 }
17664                 
17665                 return true;
17666             },
17667
17668             scope : this,
17669
17670             doRelay : function(foo, bar, hname){
17671                 if(hname == 'down' || this.scope.isExpanded()){
17672                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17673                 }
17674                 return true;
17675             },
17676
17677             forceKeyDown: true
17678         });
17679         
17680         
17681         this.queryDelay = Math.max(this.queryDelay || 10,
17682                 this.mode == 'local' ? 10 : 250);
17683         
17684         
17685         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17686         
17687         if(this.typeAhead){
17688             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17689         }
17690         if(this.editable !== false){
17691             this.inputEl().on("keyup", this.onKeyUp, this);
17692         }
17693         if(this.forceSelection){
17694             this.inputEl().on('blur', this.doForce, this);
17695         }
17696         
17697         if(this.multiple){
17698             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17699             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17700         }
17701     },
17702     
17703     initTickableEvents: function()
17704     {   
17705         this.createList();
17706         
17707         if(this.hiddenName){
17708             
17709             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17710             
17711             this.hiddenField.dom.value =
17712                 this.hiddenValue !== undefined ? this.hiddenValue :
17713                 this.value !== undefined ? this.value : '';
17714
17715             // prevent input submission
17716             this.el.dom.removeAttribute('name');
17717             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17718              
17719              
17720         }
17721         
17722 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17723         
17724         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17725         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17726         if(this.triggerList){
17727             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17728         }
17729          
17730         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17731         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17732         
17733         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17734         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17735         
17736         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17737         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17738         
17739         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17740         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17741         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17742         
17743         this.okBtn.hide();
17744         this.cancelBtn.hide();
17745         
17746         var _this = this;
17747         
17748         (function(){
17749             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17750             _this.list.setWidth(lw);
17751         }).defer(100);
17752         
17753         this.list.on('mouseover', this.onViewOver, this);
17754         this.list.on('mousemove', this.onViewMove, this);
17755         
17756         this.list.on('scroll', this.onViewScroll, this);
17757         
17758         if(!this.tpl){
17759             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17760                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17761         }
17762
17763         this.view = new Roo.View(this.list, this.tpl, {
17764             singleSelect:true,
17765             tickable:true,
17766             parent:this,
17767             store: this.store,
17768             selectedClass: this.selectedClass
17769         });
17770         
17771         //this.view.wrapEl.setDisplayed(false);
17772         this.view.on('click', this.onViewClick, this);
17773         
17774         
17775         
17776         this.store.on('beforeload', this.onBeforeLoad, this);
17777         this.store.on('load', this.onLoad, this);
17778         this.store.on('loadexception', this.onLoadException, this);
17779         
17780         if(this.editable){
17781             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17782                 "up" : function(e){
17783                     this.inKeyMode = true;
17784                     this.selectPrev();
17785                 },
17786
17787                 "down" : function(e){
17788                     this.inKeyMode = true;
17789                     this.selectNext();
17790                 },
17791
17792                 "enter" : function(e){
17793                     if(this.fireEvent("specialkey", this, e)){
17794                         this.onViewClick(false);
17795                     }
17796                     
17797                     return true;
17798                 },
17799
17800                 "esc" : function(e){
17801                     this.onTickableFooterButtonClick(e, false, false);
17802                 },
17803
17804                 "tab" : function(e){
17805                     this.fireEvent("specialkey", this, e);
17806                     
17807                     this.onTickableFooterButtonClick(e, false, false);
17808                     
17809                     return true;
17810                 },
17811
17812                 scope : this,
17813
17814                 doRelay : function(e, fn, key){
17815                     if(this.scope.isExpanded()){
17816                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17817                     }
17818                     return true;
17819                 },
17820
17821                 forceKeyDown: true
17822             });
17823         }
17824         
17825         this.queryDelay = Math.max(this.queryDelay || 10,
17826                 this.mode == 'local' ? 10 : 250);
17827         
17828         
17829         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17830         
17831         if(this.typeAhead){
17832             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17833         }
17834         
17835         if(this.editable !== false){
17836             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17837         }
17838         
17839         this.indicator = this.indicatorEl();
17840         
17841         if(this.indicator){
17842             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17843             this.indicator.hide();
17844         }
17845         
17846     },
17847
17848     onDestroy : function(){
17849         if(this.view){
17850             this.view.setStore(null);
17851             this.view.el.removeAllListeners();
17852             this.view.el.remove();
17853             this.view.purgeListeners();
17854         }
17855         if(this.list){
17856             this.list.dom.innerHTML  = '';
17857         }
17858         
17859         if(this.store){
17860             this.store.un('beforeload', this.onBeforeLoad, this);
17861             this.store.un('load', this.onLoad, this);
17862             this.store.un('loadexception', this.onLoadException, this);
17863         }
17864         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17865     },
17866
17867     // private
17868     fireKey : function(e){
17869         if(e.isNavKeyPress() && !this.list.isVisible()){
17870             this.fireEvent("specialkey", this, e);
17871         }
17872     },
17873
17874     // private
17875     onResize: function(w, h)
17876     {
17877         
17878         
17879 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17880 //        
17881 //        if(typeof w != 'number'){
17882 //            // we do not handle it!?!?
17883 //            return;
17884 //        }
17885 //        var tw = this.trigger.getWidth();
17886 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17887 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17888 //        var x = w - tw;
17889 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17890 //            
17891 //        //this.trigger.setStyle('left', x+'px');
17892 //        
17893 //        if(this.list && this.listWidth === undefined){
17894 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17895 //            this.list.setWidth(lw);
17896 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17897 //        }
17898         
17899     
17900         
17901     },
17902
17903     /**
17904      * Allow or prevent the user from directly editing the field text.  If false is passed,
17905      * the user will only be able to select from the items defined in the dropdown list.  This method
17906      * is the runtime equivalent of setting the 'editable' config option at config time.
17907      * @param {Boolean} value True to allow the user to directly edit the field text
17908      */
17909     setEditable : function(value){
17910         if(value == this.editable){
17911             return;
17912         }
17913         this.editable = value;
17914         if(!value){
17915             this.inputEl().dom.setAttribute('readOnly', true);
17916             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17917             this.inputEl().addClass('x-combo-noedit');
17918         }else{
17919             this.inputEl().dom.removeAttribute('readOnly');
17920             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17921             this.inputEl().removeClass('x-combo-noedit');
17922         }
17923     },
17924
17925     // private
17926     
17927     onBeforeLoad : function(combo,opts){
17928         if(!this.hasFocus){
17929             return;
17930         }
17931          if (!opts.add) {
17932             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17933          }
17934         this.restrictHeight();
17935         this.selectedIndex = -1;
17936     },
17937
17938     // private
17939     onLoad : function(){
17940         
17941         this.hasQuery = false;
17942         
17943         if(!this.hasFocus){
17944             return;
17945         }
17946         
17947         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17948             this.loading.hide();
17949         }
17950         
17951         if(this.store.getCount() > 0){
17952             
17953             this.expand();
17954             this.restrictHeight();
17955             if(this.lastQuery == this.allQuery){
17956                 if(this.editable && !this.tickable){
17957                     this.inputEl().dom.select();
17958                 }
17959                 
17960                 if(
17961                     !this.selectByValue(this.value, true) &&
17962                     this.autoFocus && 
17963                     (
17964                         !this.store.lastOptions ||
17965                         typeof(this.store.lastOptions.add) == 'undefined' || 
17966                         this.store.lastOptions.add != true
17967                     )
17968                 ){
17969                     this.select(0, true);
17970                 }
17971             }else{
17972                 if(this.autoFocus){
17973                     this.selectNext();
17974                 }
17975                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17976                     this.taTask.delay(this.typeAheadDelay);
17977                 }
17978             }
17979         }else{
17980             this.onEmptyResults();
17981         }
17982         
17983         //this.el.focus();
17984     },
17985     // private
17986     onLoadException : function()
17987     {
17988         this.hasQuery = false;
17989         
17990         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17991             this.loading.hide();
17992         }
17993         
17994         if(this.tickable && this.editable){
17995             return;
17996         }
17997         
17998         this.collapse();
17999         // only causes errors at present
18000         //Roo.log(this.store.reader.jsonData);
18001         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18002             // fixme
18003             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18004         //}
18005         
18006         
18007     },
18008     // private
18009     onTypeAhead : function(){
18010         if(this.store.getCount() > 0){
18011             var r = this.store.getAt(0);
18012             var newValue = r.data[this.displayField];
18013             var len = newValue.length;
18014             var selStart = this.getRawValue().length;
18015             
18016             if(selStart != len){
18017                 this.setRawValue(newValue);
18018                 this.selectText(selStart, newValue.length);
18019             }
18020         }
18021     },
18022
18023     // private
18024     onSelect : function(record, index){
18025         
18026         if(this.fireEvent('beforeselect', this, record, index) !== false){
18027         
18028             this.setFromData(index > -1 ? record.data : false);
18029             
18030             this.collapse();
18031             this.fireEvent('select', this, record, index);
18032         }
18033     },
18034
18035     /**
18036      * Returns the currently selected field value or empty string if no value is set.
18037      * @return {String} value The selected value
18038      */
18039     getValue : function()
18040     {
18041         if(Roo.isIOS && this.useNativeIOS){
18042             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18043         }
18044         
18045         if(this.multiple){
18046             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18047         }
18048         
18049         if(this.valueField){
18050             return typeof this.value != 'undefined' ? this.value : '';
18051         }else{
18052             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
18053         }
18054     },
18055     
18056     getRawValue : function()
18057     {
18058         if(Roo.isIOS && this.useNativeIOS){
18059             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18060         }
18061         
18062         var v = this.inputEl().getValue();
18063         
18064         return v;
18065     },
18066
18067     /**
18068      * Clears any text/value currently set in the field
18069      */
18070     clearValue : function(){
18071         
18072         if(this.hiddenField){
18073             this.hiddenField.dom.value = '';
18074         }
18075         this.value = '';
18076         this.setRawValue('');
18077         this.lastSelectionText = '';
18078         this.lastData = false;
18079         
18080         var close = this.closeTriggerEl();
18081         
18082         if(close){
18083             close.hide();
18084         }
18085         
18086         this.validate();
18087         
18088     },
18089
18090     /**
18091      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18092      * will be displayed in the field.  If the value does not match the data value of an existing item,
18093      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18094      * Otherwise the field will be blank (although the value will still be set).
18095      * @param {String} value The value to match
18096      */
18097     setValue : function(v)
18098     {
18099         if(Roo.isIOS && this.useNativeIOS){
18100             this.setIOSValue(v);
18101             return;
18102         }
18103         
18104         if(this.multiple){
18105             this.syncValue();
18106             return;
18107         }
18108         
18109         var text = v;
18110         if(this.valueField){
18111             var r = this.findRecord(this.valueField, v);
18112             if(r){
18113                 text = r.data[this.displayField];
18114             }else if(this.valueNotFoundText !== undefined){
18115                 text = this.valueNotFoundText;
18116             }
18117         }
18118         this.lastSelectionText = text;
18119         if(this.hiddenField){
18120             this.hiddenField.dom.value = v;
18121         }
18122         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
18123         this.value = v;
18124         
18125         var close = this.closeTriggerEl();
18126         
18127         if(close){
18128             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18129         }
18130         
18131         this.validate();
18132     },
18133     /**
18134      * @property {Object} the last set data for the element
18135      */
18136     
18137     lastData : false,
18138     /**
18139      * Sets the value of the field based on a object which is related to the record format for the store.
18140      * @param {Object} value the value to set as. or false on reset?
18141      */
18142     setFromData : function(o){
18143         
18144         if(this.multiple){
18145             this.addItem(o);
18146             return;
18147         }
18148             
18149         var dv = ''; // display value
18150         var vv = ''; // value value..
18151         this.lastData = o;
18152         if (this.displayField) {
18153             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18154         } else {
18155             // this is an error condition!!!
18156             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18157         }
18158         
18159         if(this.valueField){
18160             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18161         }
18162         
18163         var close = this.closeTriggerEl();
18164         
18165         if(close){
18166             if(dv.length || vv * 1 > 0){
18167                 close.show() ;
18168                 this.blockFocus=true;
18169             } else {
18170                 close.hide();
18171             }             
18172         }
18173         
18174         if(this.hiddenField){
18175             this.hiddenField.dom.value = vv;
18176             
18177             this.lastSelectionText = dv;
18178             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18179             this.value = vv;
18180             return;
18181         }
18182         // no hidden field.. - we store the value in 'value', but still display
18183         // display field!!!!
18184         this.lastSelectionText = dv;
18185         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
18186         this.value = vv;
18187         
18188         
18189         
18190     },
18191     // private
18192     reset : function(){
18193         // overridden so that last data is reset..
18194         
18195         if(this.multiple){
18196             this.clearItem();
18197             return;
18198         }
18199         
18200         this.setValue(this.originalValue);
18201         //this.clearInvalid();
18202         this.lastData = false;
18203         if (this.view) {
18204             this.view.clearSelections();
18205         }
18206         
18207         this.validate();
18208     },
18209     // private
18210     findRecord : function(prop, value){
18211         var record;
18212         if(this.store.getCount() > 0){
18213             this.store.each(function(r){
18214                 if(r.data[prop] == value){
18215                     record = r;
18216                     return false;
18217                 }
18218                 return true;
18219             });
18220         }
18221         return record;
18222     },
18223     
18224     getName: function()
18225     {
18226         // returns hidden if it's set..
18227         if (!this.rendered) {return ''};
18228         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18229         
18230     },
18231     // private
18232     onViewMove : function(e, t){
18233         this.inKeyMode = false;
18234     },
18235
18236     // private
18237     onViewOver : function(e, t){
18238         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18239             return;
18240         }
18241         var item = this.view.findItemFromChild(t);
18242         
18243         if(item){
18244             var index = this.view.indexOf(item);
18245             this.select(index, false);
18246         }
18247     },
18248
18249     // private
18250     onViewClick : function(view, doFocus, el, e)
18251     {
18252         var index = this.view.getSelectedIndexes()[0];
18253         
18254         var r = this.store.getAt(index);
18255         
18256         if(this.tickable){
18257             
18258             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18259                 return;
18260             }
18261             
18262             var rm = false;
18263             var _this = this;
18264             
18265             Roo.each(this.tickItems, function(v,k){
18266                 
18267                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18268                     Roo.log(v);
18269                     _this.tickItems.splice(k, 1);
18270                     
18271                     if(typeof(e) == 'undefined' && view == false){
18272                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18273                     }
18274                     
18275                     rm = true;
18276                     return;
18277                 }
18278             });
18279             
18280             if(rm){
18281                 return;
18282             }
18283             
18284             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18285                 this.tickItems.push(r.data);
18286             }
18287             
18288             if(typeof(e) == 'undefined' && view == false){
18289                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18290             }
18291                     
18292             return;
18293         }
18294         
18295         if(r){
18296             this.onSelect(r, index);
18297         }
18298         if(doFocus !== false && !this.blockFocus){
18299             this.inputEl().focus();
18300         }
18301     },
18302
18303     // private
18304     restrictHeight : function(){
18305         //this.innerList.dom.style.height = '';
18306         //var inner = this.innerList.dom;
18307         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18308         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18309         //this.list.beginUpdate();
18310         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18311         this.list.alignTo(this.inputEl(), this.listAlign);
18312         this.list.alignTo(this.inputEl(), this.listAlign);
18313         //this.list.endUpdate();
18314     },
18315
18316     // private
18317     onEmptyResults : function(){
18318         
18319         if(this.tickable && this.editable){
18320             this.hasFocus = false;
18321             this.restrictHeight();
18322             return;
18323         }
18324         
18325         this.collapse();
18326     },
18327
18328     /**
18329      * Returns true if the dropdown list is expanded, else false.
18330      */
18331     isExpanded : function(){
18332         return this.list.isVisible();
18333     },
18334
18335     /**
18336      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18337      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18338      * @param {String} value The data value of the item to select
18339      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18340      * selected item if it is not currently in view (defaults to true)
18341      * @return {Boolean} True if the value matched an item in the list, else false
18342      */
18343     selectByValue : function(v, scrollIntoView){
18344         if(v !== undefined && v !== null){
18345             var r = this.findRecord(this.valueField || this.displayField, v);
18346             if(r){
18347                 this.select(this.store.indexOf(r), scrollIntoView);
18348                 return true;
18349             }
18350         }
18351         return false;
18352     },
18353
18354     /**
18355      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18356      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18357      * @param {Number} index The zero-based index of the list item to select
18358      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18359      * selected item if it is not currently in view (defaults to true)
18360      */
18361     select : function(index, scrollIntoView){
18362         this.selectedIndex = index;
18363         this.view.select(index);
18364         if(scrollIntoView !== false){
18365             var el = this.view.getNode(index);
18366             /*
18367              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18368              */
18369             if(el){
18370                 this.list.scrollChildIntoView(el, false);
18371             }
18372         }
18373     },
18374
18375     // private
18376     selectNext : function(){
18377         var ct = this.store.getCount();
18378         if(ct > 0){
18379             if(this.selectedIndex == -1){
18380                 this.select(0);
18381             }else if(this.selectedIndex < ct-1){
18382                 this.select(this.selectedIndex+1);
18383             }
18384         }
18385     },
18386
18387     // private
18388     selectPrev : function(){
18389         var ct = this.store.getCount();
18390         if(ct > 0){
18391             if(this.selectedIndex == -1){
18392                 this.select(0);
18393             }else if(this.selectedIndex != 0){
18394                 this.select(this.selectedIndex-1);
18395             }
18396         }
18397     },
18398
18399     // private
18400     onKeyUp : function(e){
18401         if(this.editable !== false && !e.isSpecialKey()){
18402             this.lastKey = e.getKey();
18403             this.dqTask.delay(this.queryDelay);
18404         }
18405     },
18406
18407     // private
18408     validateBlur : function(){
18409         return !this.list || !this.list.isVisible();   
18410     },
18411
18412     // private
18413     initQuery : function(){
18414         
18415         var v = this.getRawValue();
18416         
18417         if(this.tickable && this.editable){
18418             v = this.tickableInputEl().getValue();
18419         }
18420         
18421         this.doQuery(v);
18422     },
18423
18424     // private
18425     doForce : function(){
18426         if(this.inputEl().dom.value.length > 0){
18427             this.inputEl().dom.value =
18428                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18429              
18430         }
18431     },
18432
18433     /**
18434      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18435      * query allowing the query action to be canceled if needed.
18436      * @param {String} query The SQL query to execute
18437      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18438      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18439      * saved in the current store (defaults to false)
18440      */
18441     doQuery : function(q, forceAll){
18442         
18443         if(q === undefined || q === null){
18444             q = '';
18445         }
18446         var qe = {
18447             query: q,
18448             forceAll: forceAll,
18449             combo: this,
18450             cancel:false
18451         };
18452         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18453             return false;
18454         }
18455         q = qe.query;
18456         
18457         forceAll = qe.forceAll;
18458         if(forceAll === true || (q.length >= this.minChars)){
18459             
18460             this.hasQuery = true;
18461             
18462             if(this.lastQuery != q || this.alwaysQuery){
18463                 this.lastQuery = q;
18464                 if(this.mode == 'local'){
18465                     this.selectedIndex = -1;
18466                     if(forceAll){
18467                         this.store.clearFilter();
18468                     }else{
18469                         
18470                         if(this.specialFilter){
18471                             this.fireEvent('specialfilter', this);
18472                             this.onLoad();
18473                             return;
18474                         }
18475                         
18476                         this.store.filter(this.displayField, q);
18477                     }
18478                     
18479                     this.store.fireEvent("datachanged", this.store);
18480                     
18481                     this.onLoad();
18482                     
18483                     
18484                 }else{
18485                     
18486                     this.store.baseParams[this.queryParam] = q;
18487                     
18488                     var options = {params : this.getParams(q)};
18489                     
18490                     if(this.loadNext){
18491                         options.add = true;
18492                         options.params.start = this.page * this.pageSize;
18493                     }
18494                     
18495                     this.store.load(options);
18496                     
18497                     /*
18498                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18499                      *  we should expand the list on onLoad
18500                      *  so command out it
18501                      */
18502 //                    this.expand();
18503                 }
18504             }else{
18505                 this.selectedIndex = -1;
18506                 this.onLoad();   
18507             }
18508         }
18509         
18510         this.loadNext = false;
18511     },
18512     
18513     // private
18514     getParams : function(q){
18515         var p = {};
18516         //p[this.queryParam] = q;
18517         
18518         if(this.pageSize){
18519             p.start = 0;
18520             p.limit = this.pageSize;
18521         }
18522         return p;
18523     },
18524
18525     /**
18526      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18527      */
18528     collapse : function(){
18529         if(!this.isExpanded()){
18530             return;
18531         }
18532         
18533         this.list.hide();
18534         
18535         this.hasFocus = false;
18536         
18537         if(this.tickable){
18538             this.okBtn.hide();
18539             this.cancelBtn.hide();
18540             this.trigger.show();
18541             
18542             if(this.editable){
18543                 this.tickableInputEl().dom.value = '';
18544                 this.tickableInputEl().blur();
18545             }
18546             
18547         }
18548         
18549         Roo.get(document).un('mousedown', this.collapseIf, this);
18550         Roo.get(document).un('mousewheel', this.collapseIf, this);
18551         if (!this.editable) {
18552             Roo.get(document).un('keydown', this.listKeyPress, this);
18553         }
18554         this.fireEvent('collapse', this);
18555         
18556         this.validate();
18557     },
18558
18559     // private
18560     collapseIf : function(e){
18561         var in_combo  = e.within(this.el);
18562         var in_list =  e.within(this.list);
18563         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18564         
18565         if (in_combo || in_list || is_list) {
18566             //e.stopPropagation();
18567             return;
18568         }
18569         
18570         if(this.tickable){
18571             this.onTickableFooterButtonClick(e, false, false);
18572         }
18573
18574         this.collapse();
18575         
18576     },
18577
18578     /**
18579      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18580      */
18581     expand : function(){
18582        
18583         if(this.isExpanded() || !this.hasFocus){
18584             return;
18585         }
18586         
18587         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18588         this.list.setWidth(lw);
18589         
18590         Roo.log('expand');
18591         
18592         this.list.show();
18593         
18594         this.restrictHeight();
18595         
18596         if(this.tickable){
18597             
18598             this.tickItems = Roo.apply([], this.item);
18599             
18600             this.okBtn.show();
18601             this.cancelBtn.show();
18602             this.trigger.hide();
18603             
18604             if(this.editable){
18605                 this.tickableInputEl().focus();
18606             }
18607             
18608         }
18609         
18610         Roo.get(document).on('mousedown', this.collapseIf, this);
18611         Roo.get(document).on('mousewheel', this.collapseIf, this);
18612         if (!this.editable) {
18613             Roo.get(document).on('keydown', this.listKeyPress, this);
18614         }
18615         
18616         this.fireEvent('expand', this);
18617     },
18618
18619     // private
18620     // Implements the default empty TriggerField.onTriggerClick function
18621     onTriggerClick : function(e)
18622     {
18623         Roo.log('trigger click');
18624         
18625         if(this.disabled || !this.triggerList){
18626             return;
18627         }
18628         
18629         this.page = 0;
18630         this.loadNext = false;
18631         
18632         if(this.isExpanded()){
18633             this.collapse();
18634             if (!this.blockFocus) {
18635                 this.inputEl().focus();
18636             }
18637             
18638         }else {
18639             this.hasFocus = true;
18640             if(this.triggerAction == 'all') {
18641                 this.doQuery(this.allQuery, true);
18642             } else {
18643                 this.doQuery(this.getRawValue());
18644             }
18645             if (!this.blockFocus) {
18646                 this.inputEl().focus();
18647             }
18648         }
18649     },
18650     
18651     onTickableTriggerClick : function(e)
18652     {
18653         if(this.disabled){
18654             return;
18655         }
18656         
18657         this.page = 0;
18658         this.loadNext = false;
18659         this.hasFocus = true;
18660         
18661         if(this.triggerAction == 'all') {
18662             this.doQuery(this.allQuery, true);
18663         } else {
18664             this.doQuery(this.getRawValue());
18665         }
18666     },
18667     
18668     onSearchFieldClick : function(e)
18669     {
18670         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18671             this.onTickableFooterButtonClick(e, false, false);
18672             return;
18673         }
18674         
18675         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18676             return;
18677         }
18678         
18679         this.page = 0;
18680         this.loadNext = false;
18681         this.hasFocus = true;
18682         
18683         if(this.triggerAction == 'all') {
18684             this.doQuery(this.allQuery, true);
18685         } else {
18686             this.doQuery(this.getRawValue());
18687         }
18688     },
18689     
18690     listKeyPress : function(e)
18691     {
18692         //Roo.log('listkeypress');
18693         // scroll to first matching element based on key pres..
18694         if (e.isSpecialKey()) {
18695             return false;
18696         }
18697         var k = String.fromCharCode(e.getKey()).toUpperCase();
18698         //Roo.log(k);
18699         var match  = false;
18700         var csel = this.view.getSelectedNodes();
18701         var cselitem = false;
18702         if (csel.length) {
18703             var ix = this.view.indexOf(csel[0]);
18704             cselitem  = this.store.getAt(ix);
18705             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18706                 cselitem = false;
18707             }
18708             
18709         }
18710         
18711         this.store.each(function(v) { 
18712             if (cselitem) {
18713                 // start at existing selection.
18714                 if (cselitem.id == v.id) {
18715                     cselitem = false;
18716                 }
18717                 return true;
18718             }
18719                 
18720             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18721                 match = this.store.indexOf(v);
18722                 return false;
18723             }
18724             return true;
18725         }, this);
18726         
18727         if (match === false) {
18728             return true; // no more action?
18729         }
18730         // scroll to?
18731         this.view.select(match);
18732         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18733         sn.scrollIntoView(sn.dom.parentNode, false);
18734     },
18735     
18736     onViewScroll : function(e, t){
18737         
18738         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){
18739             return;
18740         }
18741         
18742         this.hasQuery = true;
18743         
18744         this.loading = this.list.select('.loading', true).first();
18745         
18746         if(this.loading === null){
18747             this.list.createChild({
18748                 tag: 'div',
18749                 cls: 'loading roo-select2-more-results roo-select2-active',
18750                 html: 'Loading more results...'
18751             });
18752             
18753             this.loading = this.list.select('.loading', true).first();
18754             
18755             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18756             
18757             this.loading.hide();
18758         }
18759         
18760         this.loading.show();
18761         
18762         var _combo = this;
18763         
18764         this.page++;
18765         this.loadNext = true;
18766         
18767         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18768         
18769         return;
18770     },
18771     
18772     addItem : function(o)
18773     {   
18774         var dv = ''; // display value
18775         
18776         if (this.displayField) {
18777             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18778         } else {
18779             // this is an error condition!!!
18780             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18781         }
18782         
18783         if(!dv.length){
18784             return;
18785         }
18786         
18787         var choice = this.choices.createChild({
18788             tag: 'li',
18789             cls: 'roo-select2-search-choice',
18790             cn: [
18791                 {
18792                     tag: 'div',
18793                     html: dv
18794                 },
18795                 {
18796                     tag: 'a',
18797                     href: '#',
18798                     cls: 'roo-select2-search-choice-close fa fa-times',
18799                     tabindex: '-1'
18800                 }
18801             ]
18802             
18803         }, this.searchField);
18804         
18805         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18806         
18807         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18808         
18809         this.item.push(o);
18810         
18811         this.lastData = o;
18812         
18813         this.syncValue();
18814         
18815         this.inputEl().dom.value = '';
18816         
18817         this.validate();
18818     },
18819     
18820     onRemoveItem : function(e, _self, o)
18821     {
18822         e.preventDefault();
18823         
18824         this.lastItem = Roo.apply([], this.item);
18825         
18826         var index = this.item.indexOf(o.data) * 1;
18827         
18828         if( index < 0){
18829             Roo.log('not this item?!');
18830             return;
18831         }
18832         
18833         this.item.splice(index, 1);
18834         o.item.remove();
18835         
18836         this.syncValue();
18837         
18838         this.fireEvent('remove', this, e);
18839         
18840         this.validate();
18841         
18842     },
18843     
18844     syncValue : function()
18845     {
18846         if(!this.item.length){
18847             this.clearValue();
18848             return;
18849         }
18850             
18851         var value = [];
18852         var _this = this;
18853         Roo.each(this.item, function(i){
18854             if(_this.valueField){
18855                 value.push(i[_this.valueField]);
18856                 return;
18857             }
18858
18859             value.push(i);
18860         });
18861
18862         this.value = value.join(',');
18863
18864         if(this.hiddenField){
18865             this.hiddenField.dom.value = this.value;
18866         }
18867         
18868         this.store.fireEvent("datachanged", this.store);
18869         
18870         this.validate();
18871     },
18872     
18873     clearItem : function()
18874     {
18875         if(!this.multiple){
18876             return;
18877         }
18878         
18879         this.item = [];
18880         
18881         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18882            c.remove();
18883         });
18884         
18885         this.syncValue();
18886         
18887         this.validate();
18888         
18889         if(this.tickable && !Roo.isTouch){
18890             this.view.refresh();
18891         }
18892     },
18893     
18894     inputEl: function ()
18895     {
18896         if(Roo.isIOS && this.useNativeIOS){
18897             return this.el.select('select.roo-ios-select', true).first();
18898         }
18899         
18900         if(Roo.isTouch && this.mobileTouchView){
18901             return this.el.select('input.form-control',true).first();
18902         }
18903         
18904         if(this.tickable){
18905             return this.searchField;
18906         }
18907         
18908         return this.el.select('input.form-control',true).first();
18909     },
18910     
18911     onTickableFooterButtonClick : function(e, btn, el)
18912     {
18913         e.preventDefault();
18914         
18915         this.lastItem = Roo.apply([], this.item);
18916         
18917         if(btn && btn.name == 'cancel'){
18918             this.tickItems = Roo.apply([], this.item);
18919             this.collapse();
18920             return;
18921         }
18922         
18923         this.clearItem();
18924         
18925         var _this = this;
18926         
18927         Roo.each(this.tickItems, function(o){
18928             _this.addItem(o);
18929         });
18930         
18931         this.collapse();
18932         
18933     },
18934     
18935     validate : function()
18936     {
18937         if(this.getVisibilityEl().hasClass('hidden')){
18938             return true;
18939         }
18940         
18941         var v = this.getRawValue();
18942         
18943         if(this.multiple){
18944             v = this.getValue();
18945         }
18946         
18947         if(this.disabled || this.allowBlank || v.length){
18948             this.markValid();
18949             return true;
18950         }
18951         
18952         this.markInvalid();
18953         return false;
18954     },
18955     
18956     tickableInputEl : function()
18957     {
18958         if(!this.tickable || !this.editable){
18959             return this.inputEl();
18960         }
18961         
18962         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18963     },
18964     
18965     
18966     getAutoCreateTouchView : function()
18967     {
18968         var id = Roo.id();
18969         
18970         var cfg = {
18971             cls: 'form-group' //input-group
18972         };
18973         
18974         var input =  {
18975             tag: 'input',
18976             id : id,
18977             type : this.inputType,
18978             cls : 'form-control x-combo-noedit',
18979             autocomplete: 'new-password',
18980             placeholder : this.placeholder || '',
18981             readonly : true
18982         };
18983         
18984         if (this.name) {
18985             input.name = this.name;
18986         }
18987         
18988         if (this.size) {
18989             input.cls += ' input-' + this.size;
18990         }
18991         
18992         if (this.disabled) {
18993             input.disabled = true;
18994         }
18995         
18996         var inputblock = {
18997             cls : 'roo-combobox-wrap',
18998             cn : [
18999                 input
19000             ]
19001         };
19002         
19003         if(this.before){
19004             inputblock.cls += ' input-group';
19005             
19006             inputblock.cn.unshift({
19007                 tag :'span',
19008                 cls : 'input-group-addon input-group-prepend input-group-text',
19009                 html : this.before
19010             });
19011         }
19012         
19013         if(this.removable && !this.multiple){
19014             inputblock.cls += ' roo-removable';
19015             
19016             inputblock.cn.push({
19017                 tag: 'button',
19018                 html : 'x',
19019                 cls : 'roo-combo-removable-btn close'
19020             });
19021         }
19022
19023         if(this.hasFeedback && !this.allowBlank){
19024             
19025             inputblock.cls += ' has-feedback';
19026             
19027             inputblock.cn.push({
19028                 tag: 'span',
19029                 cls: 'glyphicon form-control-feedback'
19030             });
19031             
19032         }
19033         
19034         if (this.after) {
19035             
19036             inputblock.cls += (this.before) ? '' : ' input-group';
19037             
19038             inputblock.cn.push({
19039                 tag :'span',
19040                 cls : 'input-group-addon input-group-append input-group-text',
19041                 html : this.after
19042             });
19043         }
19044
19045         
19046         var ibwrap = inputblock;
19047         
19048         if(this.multiple){
19049             ibwrap = {
19050                 tag: 'ul',
19051                 cls: 'roo-select2-choices',
19052                 cn:[
19053                     {
19054                         tag: 'li',
19055                         cls: 'roo-select2-search-field',
19056                         cn: [
19057
19058                             inputblock
19059                         ]
19060                     }
19061                 ]
19062             };
19063         
19064             
19065         }
19066         
19067         var combobox = {
19068             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19069             cn: [
19070                 {
19071                     tag: 'input',
19072                     type : 'hidden',
19073                     cls: 'form-hidden-field'
19074                 },
19075                 ibwrap
19076             ]
19077         };
19078         
19079         if(!this.multiple && this.showToggleBtn){
19080             
19081             var caret = {
19082                 cls: 'caret'
19083             };
19084             
19085             if (this.caret != false) {
19086                 caret = {
19087                      tag: 'i',
19088                      cls: 'fa fa-' + this.caret
19089                 };
19090                 
19091             }
19092             
19093             combobox.cn.push({
19094                 tag :'span',
19095                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19096                 cn : [
19097                     Roo.bootstrap.version == 3 ? caret : '',
19098                     {
19099                         tag: 'span',
19100                         cls: 'combobox-clear',
19101                         cn  : [
19102                             {
19103                                 tag : 'i',
19104                                 cls: 'icon-remove'
19105                             }
19106                         ]
19107                     }
19108                 ]
19109
19110             })
19111         }
19112         
19113         if(this.multiple){
19114             combobox.cls += ' roo-select2-container-multi';
19115         }
19116         
19117         var required =  this.allowBlank ?  {
19118                     tag : 'i',
19119                     style: 'display: none'
19120                 } : {
19121                    tag : 'i',
19122                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19123                    tooltip : 'This field is required'
19124                 };
19125         
19126         var align = this.labelAlign || this.parentLabelAlign();
19127         
19128         if (align ==='left' && this.fieldLabel.length) {
19129
19130             cfg.cn = [
19131                 required,
19132                 {
19133                     tag: 'label',
19134                     cls : 'control-label col-form-label',
19135                     html : this.fieldLabel
19136
19137                 },
19138                 {
19139                     cls : 'roo-combobox-wrap ', 
19140                     cn: [
19141                         combobox
19142                     ]
19143                 }
19144             ];
19145             
19146             var labelCfg = cfg.cn[1];
19147             var contentCfg = cfg.cn[2];
19148             
19149
19150             if(this.indicatorpos == 'right'){
19151                 cfg.cn = [
19152                     {
19153                         tag: 'label',
19154                         'for' :  id,
19155                         cls : 'control-label col-form-label',
19156                         cn : [
19157                             {
19158                                 tag : 'span',
19159                                 html : this.fieldLabel
19160                             },
19161                             required
19162                         ]
19163                     },
19164                     {
19165                         cls : "roo-combobox-wrap ",
19166                         cn: [
19167                             combobox
19168                         ]
19169                     }
19170
19171                 ];
19172                 
19173                 labelCfg = cfg.cn[0];
19174                 contentCfg = cfg.cn[1];
19175             }
19176             
19177            
19178             
19179             if(this.labelWidth > 12){
19180                 labelCfg.style = "width: " + this.labelWidth + 'px';
19181             }
19182            
19183             if(this.labelWidth < 13 && this.labelmd == 0){
19184                 this.labelmd = this.labelWidth;
19185             }
19186             
19187             if(this.labellg > 0){
19188                 labelCfg.cls += ' col-lg-' + this.labellg;
19189                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19190             }
19191             
19192             if(this.labelmd > 0){
19193                 labelCfg.cls += ' col-md-' + this.labelmd;
19194                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19195             }
19196             
19197             if(this.labelsm > 0){
19198                 labelCfg.cls += ' col-sm-' + this.labelsm;
19199                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19200             }
19201             
19202             if(this.labelxs > 0){
19203                 labelCfg.cls += ' col-xs-' + this.labelxs;
19204                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19205             }
19206                 
19207                 
19208         } else if ( this.fieldLabel.length) {
19209             cfg.cn = [
19210                required,
19211                 {
19212                     tag: 'label',
19213                     cls : 'control-label',
19214                     html : this.fieldLabel
19215
19216                 },
19217                 {
19218                     cls : '', 
19219                     cn: [
19220                         combobox
19221                     ]
19222                 }
19223             ];
19224             
19225             if(this.indicatorpos == 'right'){
19226                 cfg.cn = [
19227                     {
19228                         tag: 'label',
19229                         cls : 'control-label',
19230                         html : this.fieldLabel,
19231                         cn : [
19232                             required
19233                         ]
19234                     },
19235                     {
19236                         cls : '', 
19237                         cn: [
19238                             combobox
19239                         ]
19240                     }
19241                 ];
19242             }
19243         } else {
19244             cfg.cn = combobox;    
19245         }
19246         
19247         
19248         var settings = this;
19249         
19250         ['xs','sm','md','lg'].map(function(size){
19251             if (settings[size]) {
19252                 cfg.cls += ' col-' + size + '-' + settings[size];
19253             }
19254         });
19255         
19256         return cfg;
19257     },
19258     
19259     initTouchView : function()
19260     {
19261         this.renderTouchView();
19262         
19263         this.touchViewEl.on('scroll', function(){
19264             this.el.dom.scrollTop = 0;
19265         }, this);
19266         
19267         this.originalValue = this.getValue();
19268         
19269         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19270         
19271         this.inputEl().on("click", this.showTouchView, this);
19272         if (this.triggerEl) {
19273             this.triggerEl.on("click", this.showTouchView, this);
19274         }
19275         
19276         
19277         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19278         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19279         
19280         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19281         
19282         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19283         this.store.on('load', this.onTouchViewLoad, this);
19284         this.store.on('loadexception', this.onTouchViewLoadException, this);
19285         
19286         if(this.hiddenName){
19287             
19288             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19289             
19290             this.hiddenField.dom.value =
19291                 this.hiddenValue !== undefined ? this.hiddenValue :
19292                 this.value !== undefined ? this.value : '';
19293         
19294             this.el.dom.removeAttribute('name');
19295             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19296         }
19297         
19298         if(this.multiple){
19299             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19300             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19301         }
19302         
19303         if(this.removable && !this.multiple){
19304             var close = this.closeTriggerEl();
19305             if(close){
19306                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19307                 close.on('click', this.removeBtnClick, this, close);
19308             }
19309         }
19310         /*
19311          * fix the bug in Safari iOS8
19312          */
19313         this.inputEl().on("focus", function(e){
19314             document.activeElement.blur();
19315         }, this);
19316         
19317         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19318         
19319         return;
19320         
19321         
19322     },
19323     
19324     renderTouchView : function()
19325     {
19326         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
19327         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19328         
19329         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19330         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19331         
19332         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19333         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19334         this.touchViewBodyEl.setStyle('overflow', 'auto');
19335         
19336         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19337         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19338         
19339         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19340         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19341         
19342     },
19343     
19344     showTouchView : function()
19345     {
19346         if(this.disabled){
19347             return;
19348         }
19349         
19350         this.touchViewHeaderEl.hide();
19351
19352         if(this.modalTitle.length){
19353             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19354             this.touchViewHeaderEl.show();
19355         }
19356
19357         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19358         this.touchViewEl.show();
19359
19360         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19361         
19362         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19363         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19364
19365         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19366
19367         if(this.modalTitle.length){
19368             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19369         }
19370         
19371         this.touchViewBodyEl.setHeight(bodyHeight);
19372
19373         if(this.animate){
19374             var _this = this;
19375             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19376         }else{
19377             this.touchViewEl.addClass(['in','show']);
19378         }
19379         
19380         if(this._touchViewMask){
19381             Roo.get(document.body).addClass("x-body-masked");
19382             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19383             this._touchViewMask.setStyle('z-index', 10000);
19384             this._touchViewMask.addClass('show');
19385         }
19386         
19387         this.doTouchViewQuery();
19388         
19389     },
19390     
19391     hideTouchView : function()
19392     {
19393         this.touchViewEl.removeClass(['in','show']);
19394
19395         if(this.animate){
19396             var _this = this;
19397             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19398         }else{
19399             this.touchViewEl.setStyle('display', 'none');
19400         }
19401         
19402         if(this._touchViewMask){
19403             this._touchViewMask.removeClass('show');
19404             Roo.get(document.body).removeClass("x-body-masked");
19405         }
19406     },
19407     
19408     setTouchViewValue : function()
19409     {
19410         if(this.multiple){
19411             this.clearItem();
19412         
19413             var _this = this;
19414
19415             Roo.each(this.tickItems, function(o){
19416                 this.addItem(o);
19417             }, this);
19418         }
19419         
19420         this.hideTouchView();
19421     },
19422     
19423     doTouchViewQuery : function()
19424     {
19425         var qe = {
19426             query: '',
19427             forceAll: true,
19428             combo: this,
19429             cancel:false
19430         };
19431         
19432         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19433             return false;
19434         }
19435         
19436         if(!this.alwaysQuery || this.mode == 'local'){
19437             this.onTouchViewLoad();
19438             return;
19439         }
19440         
19441         this.store.load();
19442     },
19443     
19444     onTouchViewBeforeLoad : function(combo,opts)
19445     {
19446         return;
19447     },
19448
19449     // private
19450     onTouchViewLoad : function()
19451     {
19452         if(this.store.getCount() < 1){
19453             this.onTouchViewEmptyResults();
19454             return;
19455         }
19456         
19457         this.clearTouchView();
19458         
19459         var rawValue = this.getRawValue();
19460         
19461         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19462         
19463         this.tickItems = [];
19464         
19465         this.store.data.each(function(d, rowIndex){
19466             var row = this.touchViewListGroup.createChild(template);
19467             
19468             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19469                 row.addClass(d.data.cls);
19470             }
19471             
19472             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19473                 var cfg = {
19474                     data : d.data,
19475                     html : d.data[this.displayField]
19476                 };
19477                 
19478                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19479                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19480                 }
19481             }
19482             row.removeClass('selected');
19483             if(!this.multiple && this.valueField &&
19484                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19485             {
19486                 // radio buttons..
19487                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19488                 row.addClass('selected');
19489             }
19490             
19491             if(this.multiple && this.valueField &&
19492                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19493             {
19494                 
19495                 // checkboxes...
19496                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19497                 this.tickItems.push(d.data);
19498             }
19499             
19500             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19501             
19502         }, this);
19503         
19504         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19505         
19506         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19507
19508         if(this.modalTitle.length){
19509             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19510         }
19511
19512         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19513         
19514         if(this.mobile_restrict_height && listHeight < bodyHeight){
19515             this.touchViewBodyEl.setHeight(listHeight);
19516         }
19517         
19518         var _this = this;
19519         
19520         if(firstChecked && listHeight > bodyHeight){
19521             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19522         }
19523         
19524     },
19525     
19526     onTouchViewLoadException : function()
19527     {
19528         this.hideTouchView();
19529     },
19530     
19531     onTouchViewEmptyResults : function()
19532     {
19533         this.clearTouchView();
19534         
19535         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19536         
19537         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19538         
19539     },
19540     
19541     clearTouchView : function()
19542     {
19543         this.touchViewListGroup.dom.innerHTML = '';
19544     },
19545     
19546     onTouchViewClick : function(e, el, o)
19547     {
19548         e.preventDefault();
19549         
19550         var row = o.row;
19551         var rowIndex = o.rowIndex;
19552         
19553         var r = this.store.getAt(rowIndex);
19554         
19555         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19556             
19557             if(!this.multiple){
19558                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19559                     c.dom.removeAttribute('checked');
19560                 }, this);
19561
19562                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19563
19564                 this.setFromData(r.data);
19565
19566                 var close = this.closeTriggerEl();
19567
19568                 if(close){
19569                     close.show();
19570                 }
19571
19572                 this.hideTouchView();
19573
19574                 this.fireEvent('select', this, r, rowIndex);
19575
19576                 return;
19577             }
19578
19579             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19580                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19581                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19582                 return;
19583             }
19584
19585             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19586             this.addItem(r.data);
19587             this.tickItems.push(r.data);
19588         }
19589     },
19590     
19591     getAutoCreateNativeIOS : function()
19592     {
19593         var cfg = {
19594             cls: 'form-group' //input-group,
19595         };
19596         
19597         var combobox =  {
19598             tag: 'select',
19599             cls : 'roo-ios-select'
19600         };
19601         
19602         if (this.name) {
19603             combobox.name = this.name;
19604         }
19605         
19606         if (this.disabled) {
19607             combobox.disabled = true;
19608         }
19609         
19610         var settings = this;
19611         
19612         ['xs','sm','md','lg'].map(function(size){
19613             if (settings[size]) {
19614                 cfg.cls += ' col-' + size + '-' + settings[size];
19615             }
19616         });
19617         
19618         cfg.cn = combobox;
19619         
19620         return cfg;
19621         
19622     },
19623     
19624     initIOSView : function()
19625     {
19626         this.store.on('load', this.onIOSViewLoad, this);
19627         
19628         return;
19629     },
19630     
19631     onIOSViewLoad : function()
19632     {
19633         if(this.store.getCount() < 1){
19634             return;
19635         }
19636         
19637         this.clearIOSView();
19638         
19639         if(this.allowBlank) {
19640             
19641             var default_text = '-- SELECT --';
19642             
19643             if(this.placeholder.length){
19644                 default_text = this.placeholder;
19645             }
19646             
19647             if(this.emptyTitle.length){
19648                 default_text += ' - ' + this.emptyTitle + ' -';
19649             }
19650             
19651             var opt = this.inputEl().createChild({
19652                 tag: 'option',
19653                 value : 0,
19654                 html : default_text
19655             });
19656             
19657             var o = {};
19658             o[this.valueField] = 0;
19659             o[this.displayField] = default_text;
19660             
19661             this.ios_options.push({
19662                 data : o,
19663                 el : opt
19664             });
19665             
19666         }
19667         
19668         this.store.data.each(function(d, rowIndex){
19669             
19670             var html = '';
19671             
19672             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19673                 html = d.data[this.displayField];
19674             }
19675             
19676             var value = '';
19677             
19678             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19679                 value = d.data[this.valueField];
19680             }
19681             
19682             var option = {
19683                 tag: 'option',
19684                 value : value,
19685                 html : html
19686             };
19687             
19688             if(this.value == d.data[this.valueField]){
19689                 option['selected'] = true;
19690             }
19691             
19692             var opt = this.inputEl().createChild(option);
19693             
19694             this.ios_options.push({
19695                 data : d.data,
19696                 el : opt
19697             });
19698             
19699         }, this);
19700         
19701         this.inputEl().on('change', function(){
19702            this.fireEvent('select', this);
19703         }, this);
19704         
19705     },
19706     
19707     clearIOSView: function()
19708     {
19709         this.inputEl().dom.innerHTML = '';
19710         
19711         this.ios_options = [];
19712     },
19713     
19714     setIOSValue: function(v)
19715     {
19716         this.value = v;
19717         
19718         if(!this.ios_options){
19719             return;
19720         }
19721         
19722         Roo.each(this.ios_options, function(opts){
19723            
19724            opts.el.dom.removeAttribute('selected');
19725            
19726            if(opts.data[this.valueField] != v){
19727                return;
19728            }
19729            
19730            opts.el.dom.setAttribute('selected', true);
19731            
19732         }, this);
19733     }
19734
19735     /** 
19736     * @cfg {Boolean} grow 
19737     * @hide 
19738     */
19739     /** 
19740     * @cfg {Number} growMin 
19741     * @hide 
19742     */
19743     /** 
19744     * @cfg {Number} growMax 
19745     * @hide 
19746     */
19747     /**
19748      * @hide
19749      * @method autoSize
19750      */
19751 });
19752
19753 Roo.apply(Roo.bootstrap.ComboBox,  {
19754     
19755     header : {
19756         tag: 'div',
19757         cls: 'modal-header',
19758         cn: [
19759             {
19760                 tag: 'h4',
19761                 cls: 'modal-title'
19762             }
19763         ]
19764     },
19765     
19766     body : {
19767         tag: 'div',
19768         cls: 'modal-body',
19769         cn: [
19770             {
19771                 tag: 'ul',
19772                 cls: 'list-group'
19773             }
19774         ]
19775     },
19776     
19777     listItemRadio : {
19778         tag: 'li',
19779         cls: 'list-group-item',
19780         cn: [
19781             {
19782                 tag: 'span',
19783                 cls: 'roo-combobox-list-group-item-value'
19784             },
19785             {
19786                 tag: 'div',
19787                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19788                 cn: [
19789                     {
19790                         tag: 'input',
19791                         type: 'radio'
19792                     },
19793                     {
19794                         tag: 'label'
19795                     }
19796                 ]
19797             }
19798         ]
19799     },
19800     
19801     listItemCheckbox : {
19802         tag: 'li',
19803         cls: 'list-group-item',
19804         cn: [
19805             {
19806                 tag: 'span',
19807                 cls: 'roo-combobox-list-group-item-value'
19808             },
19809             {
19810                 tag: 'div',
19811                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19812                 cn: [
19813                     {
19814                         tag: 'input',
19815                         type: 'checkbox'
19816                     },
19817                     {
19818                         tag: 'label'
19819                     }
19820                 ]
19821             }
19822         ]
19823     },
19824     
19825     emptyResult : {
19826         tag: 'div',
19827         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19828     },
19829     
19830     footer : {
19831         tag: 'div',
19832         cls: 'modal-footer',
19833         cn: [
19834             {
19835                 tag: 'div',
19836                 cls: 'row',
19837                 cn: [
19838                     {
19839                         tag: 'div',
19840                         cls: 'col-xs-6 text-left',
19841                         cn: {
19842                             tag: 'button',
19843                             cls: 'btn btn-danger roo-touch-view-cancel',
19844                             html: 'Cancel'
19845                         }
19846                     },
19847                     {
19848                         tag: 'div',
19849                         cls: 'col-xs-6 text-right',
19850                         cn: {
19851                             tag: 'button',
19852                             cls: 'btn btn-success roo-touch-view-ok',
19853                             html: 'OK'
19854                         }
19855                     }
19856                 ]
19857             }
19858         ]
19859         
19860     }
19861 });
19862
19863 Roo.apply(Roo.bootstrap.ComboBox,  {
19864     
19865     touchViewTemplate : {
19866         tag: 'div',
19867         cls: 'modal fade roo-combobox-touch-view',
19868         cn: [
19869             {
19870                 tag: 'div',
19871                 cls: 'modal-dialog',
19872                 style : 'position:fixed', // we have to fix position....
19873                 cn: [
19874                     {
19875                         tag: 'div',
19876                         cls: 'modal-content',
19877                         cn: [
19878                             Roo.bootstrap.ComboBox.header,
19879                             Roo.bootstrap.ComboBox.body,
19880                             Roo.bootstrap.ComboBox.footer
19881                         ]
19882                     }
19883                 ]
19884             }
19885         ]
19886     }
19887 });/*
19888  * Based on:
19889  * Ext JS Library 1.1.1
19890  * Copyright(c) 2006-2007, Ext JS, LLC.
19891  *
19892  * Originally Released Under LGPL - original licence link has changed is not relivant.
19893  *
19894  * Fork - LGPL
19895  * <script type="text/javascript">
19896  */
19897
19898 /**
19899  * @class Roo.View
19900  * @extends Roo.util.Observable
19901  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19902  * This class also supports single and multi selection modes. <br>
19903  * Create a data model bound view:
19904  <pre><code>
19905  var store = new Roo.data.Store(...);
19906
19907  var view = new Roo.View({
19908     el : "my-element",
19909     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19910  
19911     singleSelect: true,
19912     selectedClass: "ydataview-selected",
19913     store: store
19914  });
19915
19916  // listen for node click?
19917  view.on("click", function(vw, index, node, e){
19918  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19919  });
19920
19921  // load XML data
19922  dataModel.load("foobar.xml");
19923  </code></pre>
19924  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19925  * <br><br>
19926  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19927  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19928  * 
19929  * Note: old style constructor is still suported (container, template, config)
19930  * 
19931  * @constructor
19932  * Create a new View
19933  * @param {Object} config The config object
19934  * 
19935  */
19936 Roo.View = function(config, depreciated_tpl, depreciated_config){
19937     
19938     this.parent = false;
19939     
19940     if (typeof(depreciated_tpl) == 'undefined') {
19941         // new way.. - universal constructor.
19942         Roo.apply(this, config);
19943         this.el  = Roo.get(this.el);
19944     } else {
19945         // old format..
19946         this.el  = Roo.get(config);
19947         this.tpl = depreciated_tpl;
19948         Roo.apply(this, depreciated_config);
19949     }
19950     this.wrapEl  = this.el.wrap().wrap();
19951     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19952     
19953     
19954     if(typeof(this.tpl) == "string"){
19955         this.tpl = new Roo.Template(this.tpl);
19956     } else {
19957         // support xtype ctors..
19958         this.tpl = new Roo.factory(this.tpl, Roo);
19959     }
19960     
19961     
19962     this.tpl.compile();
19963     
19964     /** @private */
19965     this.addEvents({
19966         /**
19967          * @event beforeclick
19968          * Fires before a click is processed. Returns false to cancel the default action.
19969          * @param {Roo.View} this
19970          * @param {Number} index The index of the target node
19971          * @param {HTMLElement} node The target node
19972          * @param {Roo.EventObject} e The raw event object
19973          */
19974             "beforeclick" : true,
19975         /**
19976          * @event click
19977          * Fires when a template node is clicked.
19978          * @param {Roo.View} this
19979          * @param {Number} index The index of the target node
19980          * @param {HTMLElement} node The target node
19981          * @param {Roo.EventObject} e The raw event object
19982          */
19983             "click" : true,
19984         /**
19985          * @event dblclick
19986          * Fires when a template node is double clicked.
19987          * @param {Roo.View} this
19988          * @param {Number} index The index of the target node
19989          * @param {HTMLElement} node The target node
19990          * @param {Roo.EventObject} e The raw event object
19991          */
19992             "dblclick" : true,
19993         /**
19994          * @event contextmenu
19995          * Fires when a template node is right clicked.
19996          * @param {Roo.View} this
19997          * @param {Number} index The index of the target node
19998          * @param {HTMLElement} node The target node
19999          * @param {Roo.EventObject} e The raw event object
20000          */
20001             "contextmenu" : true,
20002         /**
20003          * @event selectionchange
20004          * Fires when the selected nodes change.
20005          * @param {Roo.View} this
20006          * @param {Array} selections Array of the selected nodes
20007          */
20008             "selectionchange" : true,
20009     
20010         /**
20011          * @event beforeselect
20012          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20013          * @param {Roo.View} this
20014          * @param {HTMLElement} node The node to be selected
20015          * @param {Array} selections Array of currently selected nodes
20016          */
20017             "beforeselect" : true,
20018         /**
20019          * @event preparedata
20020          * Fires on every row to render, to allow you to change the data.
20021          * @param {Roo.View} this
20022          * @param {Object} data to be rendered (change this)
20023          */
20024           "preparedata" : true
20025           
20026           
20027         });
20028
20029
20030
20031     this.el.on({
20032         "click": this.onClick,
20033         "dblclick": this.onDblClick,
20034         "contextmenu": this.onContextMenu,
20035         scope:this
20036     });
20037
20038     this.selections = [];
20039     this.nodes = [];
20040     this.cmp = new Roo.CompositeElementLite([]);
20041     if(this.store){
20042         this.store = Roo.factory(this.store, Roo.data);
20043         this.setStore(this.store, true);
20044     }
20045     
20046     if ( this.footer && this.footer.xtype) {
20047            
20048          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20049         
20050         this.footer.dataSource = this.store;
20051         this.footer.container = fctr;
20052         this.footer = Roo.factory(this.footer, Roo);
20053         fctr.insertFirst(this.el);
20054         
20055         // this is a bit insane - as the paging toolbar seems to detach the el..
20056 //        dom.parentNode.parentNode.parentNode
20057          // they get detached?
20058     }
20059     
20060     
20061     Roo.View.superclass.constructor.call(this);
20062     
20063     
20064 };
20065
20066 Roo.extend(Roo.View, Roo.util.Observable, {
20067     
20068      /**
20069      * @cfg {Roo.data.Store} store Data store to load data from.
20070      */
20071     store : false,
20072     
20073     /**
20074      * @cfg {String|Roo.Element} el The container element.
20075      */
20076     el : '',
20077     
20078     /**
20079      * @cfg {String|Roo.Template} tpl The template used by this View 
20080      */
20081     tpl : false,
20082     /**
20083      * @cfg {String} dataName the named area of the template to use as the data area
20084      *                          Works with domtemplates roo-name="name"
20085      */
20086     dataName: false,
20087     /**
20088      * @cfg {String} selectedClass The css class to add to selected nodes
20089      */
20090     selectedClass : "x-view-selected",
20091      /**
20092      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20093      */
20094     emptyText : "",
20095     
20096     /**
20097      * @cfg {String} text to display on mask (default Loading)
20098      */
20099     mask : false,
20100     /**
20101      * @cfg {Boolean} multiSelect Allow multiple selection
20102      */
20103     multiSelect : false,
20104     /**
20105      * @cfg {Boolean} singleSelect Allow single selection
20106      */
20107     singleSelect:  false,
20108     
20109     /**
20110      * @cfg {Boolean} toggleSelect - selecting 
20111      */
20112     toggleSelect : false,
20113     
20114     /**
20115      * @cfg {Boolean} tickable - selecting 
20116      */
20117     tickable : false,
20118     
20119     /**
20120      * Returns the element this view is bound to.
20121      * @return {Roo.Element}
20122      */
20123     getEl : function(){
20124         return this.wrapEl;
20125     },
20126     
20127     
20128
20129     /**
20130      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20131      */
20132     refresh : function(){
20133         //Roo.log('refresh');
20134         var t = this.tpl;
20135         
20136         // if we are using something like 'domtemplate', then
20137         // the what gets used is:
20138         // t.applySubtemplate(NAME, data, wrapping data..)
20139         // the outer template then get' applied with
20140         //     the store 'extra data'
20141         // and the body get's added to the
20142         //      roo-name="data" node?
20143         //      <span class='roo-tpl-{name}'></span> ?????
20144         
20145         
20146         
20147         this.clearSelections();
20148         this.el.update("");
20149         var html = [];
20150         var records = this.store.getRange();
20151         if(records.length < 1) {
20152             
20153             // is this valid??  = should it render a template??
20154             
20155             this.el.update(this.emptyText);
20156             return;
20157         }
20158         var el = this.el;
20159         if (this.dataName) {
20160             this.el.update(t.apply(this.store.meta)); //????
20161             el = this.el.child('.roo-tpl-' + this.dataName);
20162         }
20163         
20164         for(var i = 0, len = records.length; i < len; i++){
20165             var data = this.prepareData(records[i].data, i, records[i]);
20166             this.fireEvent("preparedata", this, data, i, records[i]);
20167             
20168             var d = Roo.apply({}, data);
20169             
20170             if(this.tickable){
20171                 Roo.apply(d, {'roo-id' : Roo.id()});
20172                 
20173                 var _this = this;
20174             
20175                 Roo.each(this.parent.item, function(item){
20176                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20177                         return;
20178                     }
20179                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20180                 });
20181             }
20182             
20183             html[html.length] = Roo.util.Format.trim(
20184                 this.dataName ?
20185                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20186                     t.apply(d)
20187             );
20188         }
20189         
20190         
20191         
20192         el.update(html.join(""));
20193         this.nodes = el.dom.childNodes;
20194         this.updateIndexes(0);
20195     },
20196     
20197
20198     /**
20199      * Function to override to reformat the data that is sent to
20200      * the template for each node.
20201      * DEPRICATED - use the preparedata event handler.
20202      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20203      * a JSON object for an UpdateManager bound view).
20204      */
20205     prepareData : function(data, index, record)
20206     {
20207         this.fireEvent("preparedata", this, data, index, record);
20208         return data;
20209     },
20210
20211     onUpdate : function(ds, record){
20212         // Roo.log('on update');   
20213         this.clearSelections();
20214         var index = this.store.indexOf(record);
20215         var n = this.nodes[index];
20216         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20217         n.parentNode.removeChild(n);
20218         this.updateIndexes(index, index);
20219     },
20220
20221     
20222     
20223 // --------- FIXME     
20224     onAdd : function(ds, records, index)
20225     {
20226         //Roo.log(['on Add', ds, records, index] );        
20227         this.clearSelections();
20228         if(this.nodes.length == 0){
20229             this.refresh();
20230             return;
20231         }
20232         var n = this.nodes[index];
20233         for(var i = 0, len = records.length; i < len; i++){
20234             var d = this.prepareData(records[i].data, i, records[i]);
20235             if(n){
20236                 this.tpl.insertBefore(n, d);
20237             }else{
20238                 
20239                 this.tpl.append(this.el, d);
20240             }
20241         }
20242         this.updateIndexes(index);
20243     },
20244
20245     onRemove : function(ds, record, index){
20246        // Roo.log('onRemove');
20247         this.clearSelections();
20248         var el = this.dataName  ?
20249             this.el.child('.roo-tpl-' + this.dataName) :
20250             this.el; 
20251         
20252         el.dom.removeChild(this.nodes[index]);
20253         this.updateIndexes(index);
20254     },
20255
20256     /**
20257      * Refresh an individual node.
20258      * @param {Number} index
20259      */
20260     refreshNode : function(index){
20261         this.onUpdate(this.store, this.store.getAt(index));
20262     },
20263
20264     updateIndexes : function(startIndex, endIndex){
20265         var ns = this.nodes;
20266         startIndex = startIndex || 0;
20267         endIndex = endIndex || ns.length - 1;
20268         for(var i = startIndex; i <= endIndex; i++){
20269             ns[i].nodeIndex = i;
20270         }
20271     },
20272
20273     /**
20274      * Changes the data store this view uses and refresh the view.
20275      * @param {Store} store
20276      */
20277     setStore : function(store, initial){
20278         if(!initial && this.store){
20279             this.store.un("datachanged", this.refresh);
20280             this.store.un("add", this.onAdd);
20281             this.store.un("remove", this.onRemove);
20282             this.store.un("update", this.onUpdate);
20283             this.store.un("clear", this.refresh);
20284             this.store.un("beforeload", this.onBeforeLoad);
20285             this.store.un("load", this.onLoad);
20286             this.store.un("loadexception", this.onLoad);
20287         }
20288         if(store){
20289           
20290             store.on("datachanged", this.refresh, this);
20291             store.on("add", this.onAdd, this);
20292             store.on("remove", this.onRemove, this);
20293             store.on("update", this.onUpdate, this);
20294             store.on("clear", this.refresh, this);
20295             store.on("beforeload", this.onBeforeLoad, this);
20296             store.on("load", this.onLoad, this);
20297             store.on("loadexception", this.onLoad, this);
20298         }
20299         
20300         if(store){
20301             this.refresh();
20302         }
20303     },
20304     /**
20305      * onbeforeLoad - masks the loading area.
20306      *
20307      */
20308     onBeforeLoad : function(store,opts)
20309     {
20310          //Roo.log('onBeforeLoad');   
20311         if (!opts.add) {
20312             this.el.update("");
20313         }
20314         this.el.mask(this.mask ? this.mask : "Loading" ); 
20315     },
20316     onLoad : function ()
20317     {
20318         this.el.unmask();
20319     },
20320     
20321
20322     /**
20323      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20324      * @param {HTMLElement} node
20325      * @return {HTMLElement} The template node
20326      */
20327     findItemFromChild : function(node){
20328         var el = this.dataName  ?
20329             this.el.child('.roo-tpl-' + this.dataName,true) :
20330             this.el.dom; 
20331         
20332         if(!node || node.parentNode == el){
20333                     return node;
20334             }
20335             var p = node.parentNode;
20336             while(p && p != el){
20337             if(p.parentNode == el){
20338                 return p;
20339             }
20340             p = p.parentNode;
20341         }
20342             return null;
20343     },
20344
20345     /** @ignore */
20346     onClick : function(e){
20347         var item = this.findItemFromChild(e.getTarget());
20348         if(item){
20349             var index = this.indexOf(item);
20350             if(this.onItemClick(item, index, e) !== false){
20351                 this.fireEvent("click", this, index, item, e);
20352             }
20353         }else{
20354             this.clearSelections();
20355         }
20356     },
20357
20358     /** @ignore */
20359     onContextMenu : function(e){
20360         var item = this.findItemFromChild(e.getTarget());
20361         if(item){
20362             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20363         }
20364     },
20365
20366     /** @ignore */
20367     onDblClick : function(e){
20368         var item = this.findItemFromChild(e.getTarget());
20369         if(item){
20370             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20371         }
20372     },
20373
20374     onItemClick : function(item, index, e)
20375     {
20376         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20377             return false;
20378         }
20379         if (this.toggleSelect) {
20380             var m = this.isSelected(item) ? 'unselect' : 'select';
20381             //Roo.log(m);
20382             var _t = this;
20383             _t[m](item, true, false);
20384             return true;
20385         }
20386         if(this.multiSelect || this.singleSelect){
20387             if(this.multiSelect && e.shiftKey && this.lastSelection){
20388                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20389             }else{
20390                 this.select(item, this.multiSelect && e.ctrlKey);
20391                 this.lastSelection = item;
20392             }
20393             
20394             if(!this.tickable){
20395                 e.preventDefault();
20396             }
20397             
20398         }
20399         return true;
20400     },
20401
20402     /**
20403      * Get the number of selected nodes.
20404      * @return {Number}
20405      */
20406     getSelectionCount : function(){
20407         return this.selections.length;
20408     },
20409
20410     /**
20411      * Get the currently selected nodes.
20412      * @return {Array} An array of HTMLElements
20413      */
20414     getSelectedNodes : function(){
20415         return this.selections;
20416     },
20417
20418     /**
20419      * Get the indexes of the selected nodes.
20420      * @return {Array}
20421      */
20422     getSelectedIndexes : function(){
20423         var indexes = [], s = this.selections;
20424         for(var i = 0, len = s.length; i < len; i++){
20425             indexes.push(s[i].nodeIndex);
20426         }
20427         return indexes;
20428     },
20429
20430     /**
20431      * Clear all selections
20432      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20433      */
20434     clearSelections : function(suppressEvent){
20435         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20436             this.cmp.elements = this.selections;
20437             this.cmp.removeClass(this.selectedClass);
20438             this.selections = [];
20439             if(!suppressEvent){
20440                 this.fireEvent("selectionchange", this, this.selections);
20441             }
20442         }
20443     },
20444
20445     /**
20446      * Returns true if the passed node is selected
20447      * @param {HTMLElement/Number} node The node or node index
20448      * @return {Boolean}
20449      */
20450     isSelected : function(node){
20451         var s = this.selections;
20452         if(s.length < 1){
20453             return false;
20454         }
20455         node = this.getNode(node);
20456         return s.indexOf(node) !== -1;
20457     },
20458
20459     /**
20460      * Selects nodes.
20461      * @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
20462      * @param {Boolean} keepExisting (optional) true to keep existing selections
20463      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20464      */
20465     select : function(nodeInfo, keepExisting, suppressEvent){
20466         if(nodeInfo instanceof Array){
20467             if(!keepExisting){
20468                 this.clearSelections(true);
20469             }
20470             for(var i = 0, len = nodeInfo.length; i < len; i++){
20471                 this.select(nodeInfo[i], true, true);
20472             }
20473             return;
20474         } 
20475         var node = this.getNode(nodeInfo);
20476         if(!node || this.isSelected(node)){
20477             return; // already selected.
20478         }
20479         if(!keepExisting){
20480             this.clearSelections(true);
20481         }
20482         
20483         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20484             Roo.fly(node).addClass(this.selectedClass);
20485             this.selections.push(node);
20486             if(!suppressEvent){
20487                 this.fireEvent("selectionchange", this, this.selections);
20488             }
20489         }
20490         
20491         
20492     },
20493       /**
20494      * Unselects nodes.
20495      * @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
20496      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20497      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20498      */
20499     unselect : function(nodeInfo, keepExisting, suppressEvent)
20500     {
20501         if(nodeInfo instanceof Array){
20502             Roo.each(this.selections, function(s) {
20503                 this.unselect(s, nodeInfo);
20504             }, this);
20505             return;
20506         }
20507         var node = this.getNode(nodeInfo);
20508         if(!node || !this.isSelected(node)){
20509             //Roo.log("not selected");
20510             return; // not selected.
20511         }
20512         // fireevent???
20513         var ns = [];
20514         Roo.each(this.selections, function(s) {
20515             if (s == node ) {
20516                 Roo.fly(node).removeClass(this.selectedClass);
20517
20518                 return;
20519             }
20520             ns.push(s);
20521         },this);
20522         
20523         this.selections= ns;
20524         this.fireEvent("selectionchange", this, this.selections);
20525     },
20526
20527     /**
20528      * Gets a template node.
20529      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20530      * @return {HTMLElement} The node or null if it wasn't found
20531      */
20532     getNode : function(nodeInfo){
20533         if(typeof nodeInfo == "string"){
20534             return document.getElementById(nodeInfo);
20535         }else if(typeof nodeInfo == "number"){
20536             return this.nodes[nodeInfo];
20537         }
20538         return nodeInfo;
20539     },
20540
20541     /**
20542      * Gets a range template nodes.
20543      * @param {Number} startIndex
20544      * @param {Number} endIndex
20545      * @return {Array} An array of nodes
20546      */
20547     getNodes : function(start, end){
20548         var ns = this.nodes;
20549         start = start || 0;
20550         end = typeof end == "undefined" ? ns.length - 1 : end;
20551         var nodes = [];
20552         if(start <= end){
20553             for(var i = start; i <= end; i++){
20554                 nodes.push(ns[i]);
20555             }
20556         } else{
20557             for(var i = start; i >= end; i--){
20558                 nodes.push(ns[i]);
20559             }
20560         }
20561         return nodes;
20562     },
20563
20564     /**
20565      * Finds the index of the passed node
20566      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20567      * @return {Number} The index of the node or -1
20568      */
20569     indexOf : function(node){
20570         node = this.getNode(node);
20571         if(typeof node.nodeIndex == "number"){
20572             return node.nodeIndex;
20573         }
20574         var ns = this.nodes;
20575         for(var i = 0, len = ns.length; i < len; i++){
20576             if(ns[i] == node){
20577                 return i;
20578             }
20579         }
20580         return -1;
20581     }
20582 });
20583 /*
20584  * - LGPL
20585  *
20586  * based on jquery fullcalendar
20587  * 
20588  */
20589
20590 Roo.bootstrap = Roo.bootstrap || {};
20591 /**
20592  * @class Roo.bootstrap.Calendar
20593  * @extends Roo.bootstrap.Component
20594  * Bootstrap Calendar class
20595  * @cfg {Boolean} loadMask (true|false) default false
20596  * @cfg {Object} header generate the user specific header of the calendar, default false
20597
20598  * @constructor
20599  * Create a new Container
20600  * @param {Object} config The config object
20601  */
20602
20603
20604
20605 Roo.bootstrap.Calendar = function(config){
20606     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20607      this.addEvents({
20608         /**
20609              * @event select
20610              * Fires when a date is selected
20611              * @param {DatePicker} this
20612              * @param {Date} date The selected date
20613              */
20614         'select': true,
20615         /**
20616              * @event monthchange
20617              * Fires when the displayed month changes 
20618              * @param {DatePicker} this
20619              * @param {Date} date The selected month
20620              */
20621         'monthchange': true,
20622         /**
20623              * @event evententer
20624              * Fires when mouse over an event
20625              * @param {Calendar} this
20626              * @param {event} Event
20627              */
20628         'evententer': true,
20629         /**
20630              * @event eventleave
20631              * Fires when the mouse leaves an
20632              * @param {Calendar} this
20633              * @param {event}
20634              */
20635         'eventleave': true,
20636         /**
20637              * @event eventclick
20638              * Fires when the mouse click an
20639              * @param {Calendar} this
20640              * @param {event}
20641              */
20642         'eventclick': true
20643         
20644     });
20645
20646 };
20647
20648 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20649     
20650           /**
20651      * @cfg {Roo.data.Store} store
20652      * The data source for the calendar
20653      */
20654         store : false,
20655      /**
20656      * @cfg {Number} startDay
20657      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20658      */
20659     startDay : 0,
20660     
20661     loadMask : false,
20662     
20663     header : false,
20664       
20665     getAutoCreate : function(){
20666         
20667         
20668         var fc_button = function(name, corner, style, content ) {
20669             return Roo.apply({},{
20670                 tag : 'span',
20671                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20672                          (corner.length ?
20673                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20674                             ''
20675                         ),
20676                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20677                 unselectable: 'on'
20678             });
20679         };
20680         
20681         var header = {};
20682         
20683         if(!this.header){
20684             header = {
20685                 tag : 'table',
20686                 cls : 'fc-header',
20687                 style : 'width:100%',
20688                 cn : [
20689                     {
20690                         tag: 'tr',
20691                         cn : [
20692                             {
20693                                 tag : 'td',
20694                                 cls : 'fc-header-left',
20695                                 cn : [
20696                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20697                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20698                                     { tag: 'span', cls: 'fc-header-space' },
20699                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20700
20701
20702                                 ]
20703                             },
20704
20705                             {
20706                                 tag : 'td',
20707                                 cls : 'fc-header-center',
20708                                 cn : [
20709                                     {
20710                                         tag: 'span',
20711                                         cls: 'fc-header-title',
20712                                         cn : {
20713                                             tag: 'H2',
20714                                             html : 'month / year'
20715                                         }
20716                                     }
20717
20718                                 ]
20719                             },
20720                             {
20721                                 tag : 'td',
20722                                 cls : 'fc-header-right',
20723                                 cn : [
20724                               /*      fc_button('month', 'left', '', 'month' ),
20725                                     fc_button('week', '', '', 'week' ),
20726                                     fc_button('day', 'right', '', 'day' )
20727                                 */    
20728
20729                                 ]
20730                             }
20731
20732                         ]
20733                     }
20734                 ]
20735             };
20736         }
20737         
20738         header = this.header;
20739         
20740        
20741         var cal_heads = function() {
20742             var ret = [];
20743             // fixme - handle this.
20744             
20745             for (var i =0; i < Date.dayNames.length; i++) {
20746                 var d = Date.dayNames[i];
20747                 ret.push({
20748                     tag: 'th',
20749                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20750                     html : d.substring(0,3)
20751                 });
20752                 
20753             }
20754             ret[0].cls += ' fc-first';
20755             ret[6].cls += ' fc-last';
20756             return ret;
20757         };
20758         var cal_cell = function(n) {
20759             return  {
20760                 tag: 'td',
20761                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20762                 cn : [
20763                     {
20764                         cn : [
20765                             {
20766                                 cls: 'fc-day-number',
20767                                 html: 'D'
20768                             },
20769                             {
20770                                 cls: 'fc-day-content',
20771                              
20772                                 cn : [
20773                                      {
20774                                         style: 'position: relative;' // height: 17px;
20775                                     }
20776                                 ]
20777                             }
20778                             
20779                             
20780                         ]
20781                     }
20782                 ]
20783                 
20784             }
20785         };
20786         var cal_rows = function() {
20787             
20788             var ret = [];
20789             for (var r = 0; r < 6; r++) {
20790                 var row= {
20791                     tag : 'tr',
20792                     cls : 'fc-week',
20793                     cn : []
20794                 };
20795                 
20796                 for (var i =0; i < Date.dayNames.length; i++) {
20797                     var d = Date.dayNames[i];
20798                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20799
20800                 }
20801                 row.cn[0].cls+=' fc-first';
20802                 row.cn[0].cn[0].style = 'min-height:90px';
20803                 row.cn[6].cls+=' fc-last';
20804                 ret.push(row);
20805                 
20806             }
20807             ret[0].cls += ' fc-first';
20808             ret[4].cls += ' fc-prev-last';
20809             ret[5].cls += ' fc-last';
20810             return ret;
20811             
20812         };
20813         
20814         var cal_table = {
20815             tag: 'table',
20816             cls: 'fc-border-separate',
20817             style : 'width:100%',
20818             cellspacing  : 0,
20819             cn : [
20820                 { 
20821                     tag: 'thead',
20822                     cn : [
20823                         { 
20824                             tag: 'tr',
20825                             cls : 'fc-first fc-last',
20826                             cn : cal_heads()
20827                         }
20828                     ]
20829                 },
20830                 { 
20831                     tag: 'tbody',
20832                     cn : cal_rows()
20833                 }
20834                   
20835             ]
20836         };
20837          
20838          var cfg = {
20839             cls : 'fc fc-ltr',
20840             cn : [
20841                 header,
20842                 {
20843                     cls : 'fc-content',
20844                     style : "position: relative;",
20845                     cn : [
20846                         {
20847                             cls : 'fc-view fc-view-month fc-grid',
20848                             style : 'position: relative',
20849                             unselectable : 'on',
20850                             cn : [
20851                                 {
20852                                     cls : 'fc-event-container',
20853                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20854                                 },
20855                                 cal_table
20856                             ]
20857                         }
20858                     ]
20859     
20860                 }
20861            ] 
20862             
20863         };
20864         
20865          
20866         
20867         return cfg;
20868     },
20869     
20870     
20871     initEvents : function()
20872     {
20873         if(!this.store){
20874             throw "can not find store for calendar";
20875         }
20876         
20877         var mark = {
20878             tag: "div",
20879             cls:"x-dlg-mask",
20880             style: "text-align:center",
20881             cn: [
20882                 {
20883                     tag: "div",
20884                     style: "background-color:white;width:50%;margin:250 auto",
20885                     cn: [
20886                         {
20887                             tag: "img",
20888                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20889                         },
20890                         {
20891                             tag: "span",
20892                             html: "Loading"
20893                         }
20894                         
20895                     ]
20896                 }
20897             ]
20898         };
20899         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20900         
20901         var size = this.el.select('.fc-content', true).first().getSize();
20902         this.maskEl.setSize(size.width, size.height);
20903         this.maskEl.enableDisplayMode("block");
20904         if(!this.loadMask){
20905             this.maskEl.hide();
20906         }
20907         
20908         this.store = Roo.factory(this.store, Roo.data);
20909         this.store.on('load', this.onLoad, this);
20910         this.store.on('beforeload', this.onBeforeLoad, this);
20911         
20912         this.resize();
20913         
20914         this.cells = this.el.select('.fc-day',true);
20915         //Roo.log(this.cells);
20916         this.textNodes = this.el.query('.fc-day-number');
20917         this.cells.addClassOnOver('fc-state-hover');
20918         
20919         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20920         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20921         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20922         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20923         
20924         this.on('monthchange', this.onMonthChange, this);
20925         
20926         this.update(new Date().clearTime());
20927     },
20928     
20929     resize : function() {
20930         var sz  = this.el.getSize();
20931         
20932         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20933         this.el.select('.fc-day-content div',true).setHeight(34);
20934     },
20935     
20936     
20937     // private
20938     showPrevMonth : function(e){
20939         this.update(this.activeDate.add("mo", -1));
20940     },
20941     showToday : function(e){
20942         this.update(new Date().clearTime());
20943     },
20944     // private
20945     showNextMonth : function(e){
20946         this.update(this.activeDate.add("mo", 1));
20947     },
20948
20949     // private
20950     showPrevYear : function(){
20951         this.update(this.activeDate.add("y", -1));
20952     },
20953
20954     // private
20955     showNextYear : function(){
20956         this.update(this.activeDate.add("y", 1));
20957     },
20958
20959     
20960    // private
20961     update : function(date)
20962     {
20963         var vd = this.activeDate;
20964         this.activeDate = date;
20965 //        if(vd && this.el){
20966 //            var t = date.getTime();
20967 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20968 //                Roo.log('using add remove');
20969 //                
20970 //                this.fireEvent('monthchange', this, date);
20971 //                
20972 //                this.cells.removeClass("fc-state-highlight");
20973 //                this.cells.each(function(c){
20974 //                   if(c.dateValue == t){
20975 //                       c.addClass("fc-state-highlight");
20976 //                       setTimeout(function(){
20977 //                            try{c.dom.firstChild.focus();}catch(e){}
20978 //                       }, 50);
20979 //                       return false;
20980 //                   }
20981 //                   return true;
20982 //                });
20983 //                return;
20984 //            }
20985 //        }
20986         
20987         var days = date.getDaysInMonth();
20988         
20989         var firstOfMonth = date.getFirstDateOfMonth();
20990         var startingPos = firstOfMonth.getDay()-this.startDay;
20991         
20992         if(startingPos < this.startDay){
20993             startingPos += 7;
20994         }
20995         
20996         var pm = date.add(Date.MONTH, -1);
20997         var prevStart = pm.getDaysInMonth()-startingPos;
20998 //        
20999         this.cells = this.el.select('.fc-day',true);
21000         this.textNodes = this.el.query('.fc-day-number');
21001         this.cells.addClassOnOver('fc-state-hover');
21002         
21003         var cells = this.cells.elements;
21004         var textEls = this.textNodes;
21005         
21006         Roo.each(cells, function(cell){
21007             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21008         });
21009         
21010         days += startingPos;
21011
21012         // convert everything to numbers so it's fast
21013         var day = 86400000;
21014         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21015         //Roo.log(d);
21016         //Roo.log(pm);
21017         //Roo.log(prevStart);
21018         
21019         var today = new Date().clearTime().getTime();
21020         var sel = date.clearTime().getTime();
21021         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21022         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21023         var ddMatch = this.disabledDatesRE;
21024         var ddText = this.disabledDatesText;
21025         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21026         var ddaysText = this.disabledDaysText;
21027         var format = this.format;
21028         
21029         var setCellClass = function(cal, cell){
21030             cell.row = 0;
21031             cell.events = [];
21032             cell.more = [];
21033             //Roo.log('set Cell Class');
21034             cell.title = "";
21035             var t = d.getTime();
21036             
21037             //Roo.log(d);
21038             
21039             cell.dateValue = t;
21040             if(t == today){
21041                 cell.className += " fc-today";
21042                 cell.className += " fc-state-highlight";
21043                 cell.title = cal.todayText;
21044             }
21045             if(t == sel){
21046                 // disable highlight in other month..
21047                 //cell.className += " fc-state-highlight";
21048                 
21049             }
21050             // disabling
21051             if(t < min) {
21052                 cell.className = " fc-state-disabled";
21053                 cell.title = cal.minText;
21054                 return;
21055             }
21056             if(t > max) {
21057                 cell.className = " fc-state-disabled";
21058                 cell.title = cal.maxText;
21059                 return;
21060             }
21061             if(ddays){
21062                 if(ddays.indexOf(d.getDay()) != -1){
21063                     cell.title = ddaysText;
21064                     cell.className = " fc-state-disabled";
21065                 }
21066             }
21067             if(ddMatch && format){
21068                 var fvalue = d.dateFormat(format);
21069                 if(ddMatch.test(fvalue)){
21070                     cell.title = ddText.replace("%0", fvalue);
21071                     cell.className = " fc-state-disabled";
21072                 }
21073             }
21074             
21075             if (!cell.initialClassName) {
21076                 cell.initialClassName = cell.dom.className;
21077             }
21078             
21079             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21080         };
21081
21082         var i = 0;
21083         
21084         for(; i < startingPos; i++) {
21085             textEls[i].innerHTML = (++prevStart);
21086             d.setDate(d.getDate()+1);
21087             
21088             cells[i].className = "fc-past fc-other-month";
21089             setCellClass(this, cells[i]);
21090         }
21091         
21092         var intDay = 0;
21093         
21094         for(; i < days; i++){
21095             intDay = i - startingPos + 1;
21096             textEls[i].innerHTML = (intDay);
21097             d.setDate(d.getDate()+1);
21098             
21099             cells[i].className = ''; // "x-date-active";
21100             setCellClass(this, cells[i]);
21101         }
21102         var extraDays = 0;
21103         
21104         for(; i < 42; i++) {
21105             textEls[i].innerHTML = (++extraDays);
21106             d.setDate(d.getDate()+1);
21107             
21108             cells[i].className = "fc-future fc-other-month";
21109             setCellClass(this, cells[i]);
21110         }
21111         
21112         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21113         
21114         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21115         
21116         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21117         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21118         
21119         if(totalRows != 6){
21120             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21121             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21122         }
21123         
21124         this.fireEvent('monthchange', this, date);
21125         
21126         
21127         /*
21128         if(!this.internalRender){
21129             var main = this.el.dom.firstChild;
21130             var w = main.offsetWidth;
21131             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21132             Roo.fly(main).setWidth(w);
21133             this.internalRender = true;
21134             // opera does not respect the auto grow header center column
21135             // then, after it gets a width opera refuses to recalculate
21136             // without a second pass
21137             if(Roo.isOpera && !this.secondPass){
21138                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21139                 this.secondPass = true;
21140                 this.update.defer(10, this, [date]);
21141             }
21142         }
21143         */
21144         
21145     },
21146     
21147     findCell : function(dt) {
21148         dt = dt.clearTime().getTime();
21149         var ret = false;
21150         this.cells.each(function(c){
21151             //Roo.log("check " +c.dateValue + '?=' + dt);
21152             if(c.dateValue == dt){
21153                 ret = c;
21154                 return false;
21155             }
21156             return true;
21157         });
21158         
21159         return ret;
21160     },
21161     
21162     findCells : function(ev) {
21163         var s = ev.start.clone().clearTime().getTime();
21164        // Roo.log(s);
21165         var e= ev.end.clone().clearTime().getTime();
21166        // Roo.log(e);
21167         var ret = [];
21168         this.cells.each(function(c){
21169              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21170             
21171             if(c.dateValue > e){
21172                 return ;
21173             }
21174             if(c.dateValue < s){
21175                 return ;
21176             }
21177             ret.push(c);
21178         });
21179         
21180         return ret;    
21181     },
21182     
21183 //    findBestRow: function(cells)
21184 //    {
21185 //        var ret = 0;
21186 //        
21187 //        for (var i =0 ; i < cells.length;i++) {
21188 //            ret  = Math.max(cells[i].rows || 0,ret);
21189 //        }
21190 //        return ret;
21191 //        
21192 //    },
21193     
21194     
21195     addItem : function(ev)
21196     {
21197         // look for vertical location slot in
21198         var cells = this.findCells(ev);
21199         
21200 //        ev.row = this.findBestRow(cells);
21201         
21202         // work out the location.
21203         
21204         var crow = false;
21205         var rows = [];
21206         for(var i =0; i < cells.length; i++) {
21207             
21208             cells[i].row = cells[0].row;
21209             
21210             if(i == 0){
21211                 cells[i].row = cells[i].row + 1;
21212             }
21213             
21214             if (!crow) {
21215                 crow = {
21216                     start : cells[i],
21217                     end :  cells[i]
21218                 };
21219                 continue;
21220             }
21221             if (crow.start.getY() == cells[i].getY()) {
21222                 // on same row.
21223                 crow.end = cells[i];
21224                 continue;
21225             }
21226             // different row.
21227             rows.push(crow);
21228             crow = {
21229                 start: cells[i],
21230                 end : cells[i]
21231             };
21232             
21233         }
21234         
21235         rows.push(crow);
21236         ev.els = [];
21237         ev.rows = rows;
21238         ev.cells = cells;
21239         
21240         cells[0].events.push(ev);
21241         
21242         this.calevents.push(ev);
21243     },
21244     
21245     clearEvents: function() {
21246         
21247         if(!this.calevents){
21248             return;
21249         }
21250         
21251         Roo.each(this.cells.elements, function(c){
21252             c.row = 0;
21253             c.events = [];
21254             c.more = [];
21255         });
21256         
21257         Roo.each(this.calevents, function(e) {
21258             Roo.each(e.els, function(el) {
21259                 el.un('mouseenter' ,this.onEventEnter, this);
21260                 el.un('mouseleave' ,this.onEventLeave, this);
21261                 el.remove();
21262             },this);
21263         },this);
21264         
21265         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21266             e.remove();
21267         });
21268         
21269     },
21270     
21271     renderEvents: function()
21272     {   
21273         var _this = this;
21274         
21275         this.cells.each(function(c) {
21276             
21277             if(c.row < 5){
21278                 return;
21279             }
21280             
21281             var ev = c.events;
21282             
21283             var r = 4;
21284             if(c.row != c.events.length){
21285                 r = 4 - (4 - (c.row - c.events.length));
21286             }
21287             
21288             c.events = ev.slice(0, r);
21289             c.more = ev.slice(r);
21290             
21291             if(c.more.length && c.more.length == 1){
21292                 c.events.push(c.more.pop());
21293             }
21294             
21295             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21296             
21297         });
21298             
21299         this.cells.each(function(c) {
21300             
21301             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21302             
21303             
21304             for (var e = 0; e < c.events.length; e++){
21305                 var ev = c.events[e];
21306                 var rows = ev.rows;
21307                 
21308                 for(var i = 0; i < rows.length; i++) {
21309                 
21310                     // how many rows should it span..
21311
21312                     var  cfg = {
21313                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21314                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21315
21316                         unselectable : "on",
21317                         cn : [
21318                             {
21319                                 cls: 'fc-event-inner',
21320                                 cn : [
21321     //                                {
21322     //                                  tag:'span',
21323     //                                  cls: 'fc-event-time',
21324     //                                  html : cells.length > 1 ? '' : ev.time
21325     //                                },
21326                                     {
21327                                       tag:'span',
21328                                       cls: 'fc-event-title',
21329                                       html : String.format('{0}', ev.title)
21330                                     }
21331
21332
21333                                 ]
21334                             },
21335                             {
21336                                 cls: 'ui-resizable-handle ui-resizable-e',
21337                                 html : '&nbsp;&nbsp;&nbsp'
21338                             }
21339
21340                         ]
21341                     };
21342
21343                     if (i == 0) {
21344                         cfg.cls += ' fc-event-start';
21345                     }
21346                     if ((i+1) == rows.length) {
21347                         cfg.cls += ' fc-event-end';
21348                     }
21349
21350                     var ctr = _this.el.select('.fc-event-container',true).first();
21351                     var cg = ctr.createChild(cfg);
21352
21353                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21354                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21355
21356                     var r = (c.more.length) ? 1 : 0;
21357                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21358                     cg.setWidth(ebox.right - sbox.x -2);
21359
21360                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21361                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21362                     cg.on('click', _this.onEventClick, _this, ev);
21363
21364                     ev.els.push(cg);
21365                     
21366                 }
21367                 
21368             }
21369             
21370             
21371             if(c.more.length){
21372                 var  cfg = {
21373                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21374                     style : 'position: absolute',
21375                     unselectable : "on",
21376                     cn : [
21377                         {
21378                             cls: 'fc-event-inner',
21379                             cn : [
21380                                 {
21381                                   tag:'span',
21382                                   cls: 'fc-event-title',
21383                                   html : 'More'
21384                                 }
21385
21386
21387                             ]
21388                         },
21389                         {
21390                             cls: 'ui-resizable-handle ui-resizable-e',
21391                             html : '&nbsp;&nbsp;&nbsp'
21392                         }
21393
21394                     ]
21395                 };
21396
21397                 var ctr = _this.el.select('.fc-event-container',true).first();
21398                 var cg = ctr.createChild(cfg);
21399
21400                 var sbox = c.select('.fc-day-content',true).first().getBox();
21401                 var ebox = c.select('.fc-day-content',true).first().getBox();
21402                 //Roo.log(cg);
21403                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21404                 cg.setWidth(ebox.right - sbox.x -2);
21405
21406                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21407                 
21408             }
21409             
21410         });
21411         
21412         
21413         
21414     },
21415     
21416     onEventEnter: function (e, el,event,d) {
21417         this.fireEvent('evententer', this, el, event);
21418     },
21419     
21420     onEventLeave: function (e, el,event,d) {
21421         this.fireEvent('eventleave', this, el, event);
21422     },
21423     
21424     onEventClick: function (e, el,event,d) {
21425         this.fireEvent('eventclick', this, el, event);
21426     },
21427     
21428     onMonthChange: function () {
21429         this.store.load();
21430     },
21431     
21432     onMoreEventClick: function(e, el, more)
21433     {
21434         var _this = this;
21435         
21436         this.calpopover.placement = 'right';
21437         this.calpopover.setTitle('More');
21438         
21439         this.calpopover.setContent('');
21440         
21441         var ctr = this.calpopover.el.select('.popover-content', true).first();
21442         
21443         Roo.each(more, function(m){
21444             var cfg = {
21445                 cls : 'fc-event-hori fc-event-draggable',
21446                 html : m.title
21447             };
21448             var cg = ctr.createChild(cfg);
21449             
21450             cg.on('click', _this.onEventClick, _this, m);
21451         });
21452         
21453         this.calpopover.show(el);
21454         
21455         
21456     },
21457     
21458     onLoad: function () 
21459     {   
21460         this.calevents = [];
21461         var cal = this;
21462         
21463         if(this.store.getCount() > 0){
21464             this.store.data.each(function(d){
21465                cal.addItem({
21466                     id : d.data.id,
21467                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21468                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21469                     time : d.data.start_time,
21470                     title : d.data.title,
21471                     description : d.data.description,
21472                     venue : d.data.venue
21473                 });
21474             });
21475         }
21476         
21477         this.renderEvents();
21478         
21479         if(this.calevents.length && this.loadMask){
21480             this.maskEl.hide();
21481         }
21482     },
21483     
21484     onBeforeLoad: function()
21485     {
21486         this.clearEvents();
21487         if(this.loadMask){
21488             this.maskEl.show();
21489         }
21490     }
21491 });
21492
21493  
21494  /*
21495  * - LGPL
21496  *
21497  * element
21498  * 
21499  */
21500
21501 /**
21502  * @class Roo.bootstrap.Popover
21503  * @extends Roo.bootstrap.Component
21504  * @builder-top
21505  * @children Roo.bootstrap.Component
21506  * Bootstrap Popover class
21507  * @cfg {String} html contents of the popover   (or false to use children..)
21508  * @cfg {String} title of popover (or false to hide)
21509  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21510  * @cfg {String} trigger click || hover (or false to trigger manually)
21511  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21512  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21513  *      - if false and it has a 'parent' then it will be automatically added to that element
21514  *      - if string - Roo.get  will be called 
21515  * @cfg {Number} delay - delay before showing
21516  
21517  * @constructor
21518  * Create a new Popover
21519  * @param {Object} config The config object
21520  */
21521
21522 Roo.bootstrap.Popover = function(config){
21523     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21524     
21525     this.addEvents({
21526         // raw events
21527          /**
21528          * @event show
21529          * After the popover show
21530          * 
21531          * @param {Roo.bootstrap.Popover} this
21532          */
21533         "show" : true,
21534         /**
21535          * @event hide
21536          * After the popover hide
21537          * 
21538          * @param {Roo.bootstrap.Popover} this
21539          */
21540         "hide" : true
21541     });
21542 };
21543
21544 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21545     
21546     title: false,
21547     html: false,
21548     
21549     placement : 'right',
21550     trigger : 'hover', // hover
21551     modal : false,
21552     delay : 0,
21553     
21554     over: false,
21555     
21556     can_build_overlaid : false,
21557     
21558     maskEl : false, // the mask element
21559     headerEl : false,
21560     contentEl : false,
21561     alignEl : false, // when show is called with an element - this get's stored.
21562     
21563     getChildContainer : function()
21564     {
21565         return this.contentEl;
21566         
21567     },
21568     getPopoverHeader : function()
21569     {
21570         this.title = true; // flag not to hide it..
21571         this.headerEl.addClass('p-0');
21572         return this.headerEl
21573     },
21574     
21575     
21576     getAutoCreate : function(){
21577          
21578         var cfg = {
21579            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21580            style: 'display:block',
21581            cn : [
21582                 {
21583                     cls : 'arrow'
21584                 },
21585                 {
21586                     cls : 'popover-inner ',
21587                     cn : [
21588                         {
21589                             tag: 'h3',
21590                             cls: 'popover-title popover-header',
21591                             html : this.title === false ? '' : this.title
21592                         },
21593                         {
21594                             cls : 'popover-content popover-body '  + (this.cls || ''),
21595                             html : this.html || ''
21596                         }
21597                     ]
21598                     
21599                 }
21600            ]
21601         };
21602         
21603         return cfg;
21604     },
21605     /**
21606      * @param {string} the title
21607      */
21608     setTitle: function(str)
21609     {
21610         this.title = str;
21611         if (this.el) {
21612             this.headerEl.dom.innerHTML = str;
21613         }
21614         
21615     },
21616     /**
21617      * @param {string} the body content
21618      */
21619     setContent: function(str)
21620     {
21621         this.html = str;
21622         if (this.contentEl) {
21623             this.contentEl.dom.innerHTML = str;
21624         }
21625         
21626     },
21627     // as it get's added to the bottom of the page.
21628     onRender : function(ct, position)
21629     {
21630         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21631         
21632         
21633         
21634         if(!this.el){
21635             var cfg = Roo.apply({},  this.getAutoCreate());
21636             cfg.id = Roo.id();
21637             
21638             if (this.cls) {
21639                 cfg.cls += ' ' + this.cls;
21640             }
21641             if (this.style) {
21642                 cfg.style = this.style;
21643             }
21644             //Roo.log("adding to ");
21645             this.el = Roo.get(document.body).createChild(cfg, position);
21646 //            Roo.log(this.el);
21647         }
21648         
21649         this.contentEl = this.el.select('.popover-content',true).first();
21650         this.headerEl =  this.el.select('.popover-title',true).first();
21651         
21652         var nitems = [];
21653         if(typeof(this.items) != 'undefined'){
21654             var items = this.items;
21655             delete this.items;
21656
21657             for(var i =0;i < items.length;i++) {
21658                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21659             }
21660         }
21661
21662         this.items = nitems;
21663         
21664         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21665         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21666         
21667         
21668         
21669         this.initEvents();
21670     },
21671     
21672     resizeMask : function()
21673     {
21674         this.maskEl.setSize(
21675             Roo.lib.Dom.getViewWidth(true),
21676             Roo.lib.Dom.getViewHeight(true)
21677         );
21678     },
21679     
21680     initEvents : function()
21681     {
21682         
21683         if (!this.modal) { 
21684             Roo.bootstrap.Popover.register(this);
21685         }
21686          
21687         this.arrowEl = this.el.select('.arrow',true).first();
21688         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21689         this.el.enableDisplayMode('block');
21690         this.el.hide();
21691  
21692         
21693         if (this.over === false && !this.parent()) {
21694             return; 
21695         }
21696         if (this.triggers === false) {
21697             return;
21698         }
21699          
21700         // support parent
21701         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21702         var triggers = this.trigger ? this.trigger.split(' ') : [];
21703         Roo.each(triggers, function(trigger) {
21704         
21705             if (trigger == 'click') {
21706                 on_el.on('click', this.toggle, this);
21707             } else if (trigger != 'manual') {
21708                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21709                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21710       
21711                 on_el.on(eventIn  ,this.enter, this);
21712                 on_el.on(eventOut, this.leave, this);
21713             }
21714         }, this);
21715     },
21716     
21717     
21718     // private
21719     timeout : null,
21720     hoverState : null,
21721     
21722     toggle : function () {
21723         this.hoverState == 'in' ? this.leave() : this.enter();
21724     },
21725     
21726     enter : function () {
21727         
21728         clearTimeout(this.timeout);
21729     
21730         this.hoverState = 'in';
21731     
21732         if (!this.delay || !this.delay.show) {
21733             this.show();
21734             return;
21735         }
21736         var _t = this;
21737         this.timeout = setTimeout(function () {
21738             if (_t.hoverState == 'in') {
21739                 _t.show();
21740             }
21741         }, this.delay.show)
21742     },
21743     
21744     leave : function() {
21745         clearTimeout(this.timeout);
21746     
21747         this.hoverState = 'out';
21748     
21749         if (!this.delay || !this.delay.hide) {
21750             this.hide();
21751             return;
21752         }
21753         var _t = this;
21754         this.timeout = setTimeout(function () {
21755             if (_t.hoverState == 'out') {
21756                 _t.hide();
21757             }
21758         }, this.delay.hide)
21759     },
21760     /**
21761      * Show the popover
21762      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21763      * @param {string} (left|right|top|bottom) position
21764      */
21765     show : function (on_el, placement)
21766     {
21767         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21768         on_el = on_el || false; // default to false
21769          
21770         if (!on_el) {
21771             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21772                 on_el = this.parent().el;
21773             } else if (this.over) {
21774                 on_el = Roo.get(this.over);
21775             }
21776             
21777         }
21778         
21779         this.alignEl = Roo.get( on_el );
21780
21781         if (!this.el) {
21782             this.render(document.body);
21783         }
21784         
21785         
21786          
21787         
21788         if (this.title === false) {
21789             this.headerEl.hide();
21790         }
21791         
21792        
21793         this.el.show();
21794         this.el.dom.style.display = 'block';
21795          
21796  
21797         if (this.alignEl) {
21798             this.updatePosition(this.placement, true);
21799              
21800         } else {
21801             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21802             var es = this.el.getSize();
21803             var x = Roo.lib.Dom.getViewWidth()/2;
21804             var y = Roo.lib.Dom.getViewHeight()/2;
21805             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21806             
21807         }
21808
21809         
21810         //var arrow = this.el.select('.arrow',true).first();
21811         //arrow.set(align[2], 
21812         
21813         this.el.addClass('in');
21814         
21815          
21816         
21817         this.hoverState = 'in';
21818         
21819         if (this.modal) {
21820             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21821             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21822             this.maskEl.dom.style.display = 'block';
21823             this.maskEl.addClass('show');
21824         }
21825         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21826  
21827         this.fireEvent('show', this);
21828         
21829     },
21830     /**
21831      * fire this manually after loading a grid in the table for example
21832      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21833      * @param {Boolean} try and move it if we cant get right position.
21834      */
21835     updatePosition : function(placement, try_move)
21836     {
21837         // allow for calling with no parameters
21838         placement = placement   ? placement :  this.placement;
21839         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21840         
21841         this.el.removeClass([
21842             'fade','top','bottom', 'left', 'right','in',
21843             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21844         ]);
21845         this.el.addClass(placement + ' bs-popover-' + placement);
21846         
21847         if (!this.alignEl ) {
21848             return false;
21849         }
21850         
21851         switch (placement) {
21852             case 'right':
21853                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21854                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21855                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21856                     //normal display... or moved up/down.
21857                     this.el.setXY(offset);
21858                     var xy = this.alignEl.getAnchorXY('tr', false);
21859                     xy[0]+=2;xy[1]+=5;
21860                     this.arrowEl.setXY(xy);
21861                     return true;
21862                 }
21863                 // continue through...
21864                 return this.updatePosition('left', false);
21865                 
21866             
21867             case 'left':
21868                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21869                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21870                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21871                     //normal display... or moved up/down.
21872                     this.el.setXY(offset);
21873                     var xy = this.alignEl.getAnchorXY('tl', false);
21874                     xy[0]-=10;xy[1]+=5; // << fix me
21875                     this.arrowEl.setXY(xy);
21876                     return true;
21877                 }
21878                 // call self...
21879                 return this.updatePosition('right', false);
21880             
21881             case 'top':
21882                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21883                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21884                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21885                     //normal display... or moved up/down.
21886                     this.el.setXY(offset);
21887                     var xy = this.alignEl.getAnchorXY('t', false);
21888                     xy[1]-=10; // << fix me
21889                     this.arrowEl.setXY(xy);
21890                     return true;
21891                 }
21892                 // fall through
21893                return this.updatePosition('bottom', false);
21894             
21895             case 'bottom':
21896                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21897                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21898                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21899                     //normal display... or moved up/down.
21900                     this.el.setXY(offset);
21901                     var xy = this.alignEl.getAnchorXY('b', false);
21902                      xy[1]+=2; // << fix me
21903                     this.arrowEl.setXY(xy);
21904                     return true;
21905                 }
21906                 // fall through
21907                 return this.updatePosition('top', false);
21908                 
21909             
21910         }
21911         
21912         
21913         return false;
21914     },
21915     
21916     hide : function()
21917     {
21918         this.el.setXY([0,0]);
21919         this.el.removeClass('in');
21920         this.el.hide();
21921         this.hoverState = null;
21922         this.maskEl.hide(); // always..
21923         this.fireEvent('hide', this);
21924     }
21925     
21926 });
21927
21928
21929 Roo.apply(Roo.bootstrap.Popover, {
21930
21931     alignment : {
21932         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21933         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21934         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21935         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21936     },
21937     
21938     zIndex : 20001,
21939
21940     clickHander : false,
21941     
21942     
21943
21944     onMouseDown : function(e)
21945     {
21946         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21947             /// what is nothing is showing..
21948             this.hideAll();
21949         }
21950          
21951     },
21952     
21953     
21954     popups : [],
21955     
21956     register : function(popup)
21957     {
21958         if (!Roo.bootstrap.Popover.clickHandler) {
21959             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21960         }
21961         // hide other popups.
21962         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21963         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21964         this.hideAll(); //<< why?
21965         //this.popups.push(popup);
21966     },
21967     hideAll : function()
21968     {
21969         this.popups.forEach(function(p) {
21970             p.hide();
21971         });
21972     },
21973     onShow : function() {
21974         Roo.bootstrap.Popover.popups.push(this);
21975     },
21976     onHide : function() {
21977         Roo.bootstrap.Popover.popups.remove(this);
21978     } 
21979
21980 });/*
21981  * - LGPL
21982  *
21983  * Card header - holder for the card header elements.
21984  * 
21985  */
21986
21987 /**
21988  * @class Roo.bootstrap.PopoverNav
21989  * @extends Roo.bootstrap.nav.Simplebar
21990  * @parent Roo.bootstrap.Popover
21991  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21992  * Bootstrap Popover header navigation class
21993  * FIXME? should this go under nav?
21994  *
21995  * 
21996  * @constructor
21997  * Create a new Popover Header Navigation 
21998  * @param {Object} config The config object
21999  */
22000
22001 Roo.bootstrap.PopoverNav = function(config){
22002     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22003 };
22004
22005 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22006     
22007     
22008     container_method : 'getPopoverHeader' 
22009     
22010      
22011     
22012     
22013    
22014 });
22015
22016  
22017
22018  /*
22019  * - LGPL
22020  *
22021  * Progress
22022  * 
22023  */
22024
22025 /**
22026  * @class Roo.bootstrap.Progress
22027  * @extends Roo.bootstrap.Component
22028  * @children Roo.bootstrap.ProgressBar
22029  * Bootstrap Progress class
22030  * @cfg {Boolean} striped striped of the progress bar
22031  * @cfg {Boolean} active animated of the progress bar
22032  * 
22033  * 
22034  * @constructor
22035  * Create a new Progress
22036  * @param {Object} config The config object
22037  */
22038
22039 Roo.bootstrap.Progress = function(config){
22040     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22041 };
22042
22043 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22044     
22045     striped : false,
22046     active: false,
22047     
22048     getAutoCreate : function(){
22049         var cfg = {
22050             tag: 'div',
22051             cls: 'progress'
22052         };
22053         
22054         
22055         if(this.striped){
22056             cfg.cls += ' progress-striped';
22057         }
22058       
22059         if(this.active){
22060             cfg.cls += ' active';
22061         }
22062         
22063         
22064         return cfg;
22065     }
22066    
22067 });
22068
22069  
22070
22071  /*
22072  * - LGPL
22073  *
22074  * ProgressBar
22075  * 
22076  */
22077
22078 /**
22079  * @class Roo.bootstrap.ProgressBar
22080  * @extends Roo.bootstrap.Component
22081  * Bootstrap ProgressBar class
22082  * @cfg {Number} aria_valuenow aria-value now
22083  * @cfg {Number} aria_valuemin aria-value min
22084  * @cfg {Number} aria_valuemax aria-value max
22085  * @cfg {String} label label for the progress bar
22086  * @cfg {String} panel (success | info | warning | danger )
22087  * @cfg {String} role role of the progress bar
22088  * @cfg {String} sr_only text
22089  * 
22090  * 
22091  * @constructor
22092  * Create a new ProgressBar
22093  * @param {Object} config The config object
22094  */
22095
22096 Roo.bootstrap.ProgressBar = function(config){
22097     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22098 };
22099
22100 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22101     
22102     aria_valuenow : 0,
22103     aria_valuemin : 0,
22104     aria_valuemax : 100,
22105     label : false,
22106     panel : false,
22107     role : false,
22108     sr_only: false,
22109     
22110     getAutoCreate : function()
22111     {
22112         
22113         var cfg = {
22114             tag: 'div',
22115             cls: 'progress-bar',
22116             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22117         };
22118         
22119         if(this.sr_only){
22120             cfg.cn = {
22121                 tag: 'span',
22122                 cls: 'sr-only',
22123                 html: this.sr_only
22124             }
22125         }
22126         
22127         if(this.role){
22128             cfg.role = this.role;
22129         }
22130         
22131         if(this.aria_valuenow){
22132             cfg['aria-valuenow'] = this.aria_valuenow;
22133         }
22134         
22135         if(this.aria_valuemin){
22136             cfg['aria-valuemin'] = this.aria_valuemin;
22137         }
22138         
22139         if(this.aria_valuemax){
22140             cfg['aria-valuemax'] = this.aria_valuemax;
22141         }
22142         
22143         if(this.label && !this.sr_only){
22144             cfg.html = this.label;
22145         }
22146         
22147         if(this.panel){
22148             cfg.cls += ' progress-bar-' + this.panel;
22149         }
22150         
22151         return cfg;
22152     },
22153     
22154     update : function(aria_valuenow)
22155     {
22156         this.aria_valuenow = aria_valuenow;
22157         
22158         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22159     }
22160    
22161 });
22162
22163  
22164
22165  /**
22166  * @class Roo.bootstrap.TabGroup
22167  * @extends Roo.bootstrap.Column
22168  * @children Roo.bootstrap.TabPanel
22169  * Bootstrap Column class
22170  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22171  * @cfg {Boolean} carousel true to make the group behave like a carousel
22172  * @cfg {Boolean} bullets show bullets for the panels
22173  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22174  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22175  * @cfg {Boolean} showarrow (true|false) show arrow default true
22176  * 
22177  * @constructor
22178  * Create a new TabGroup
22179  * @param {Object} config The config object
22180  */
22181
22182 Roo.bootstrap.TabGroup = function(config){
22183     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22184     if (!this.navId) {
22185         this.navId = Roo.id();
22186     }
22187     this.tabs = [];
22188     Roo.bootstrap.TabGroup.register(this);
22189     
22190 };
22191
22192 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22193     
22194     carousel : false,
22195     transition : false,
22196     bullets : 0,
22197     timer : 0,
22198     autoslide : false,
22199     slideFn : false,
22200     slideOnTouch : false,
22201     showarrow : true,
22202     
22203     getAutoCreate : function()
22204     {
22205         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22206         
22207         cfg.cls += ' tab-content';
22208         
22209         if (this.carousel) {
22210             cfg.cls += ' carousel slide';
22211             
22212             cfg.cn = [{
22213                cls : 'carousel-inner',
22214                cn : []
22215             }];
22216         
22217             if(this.bullets  && !Roo.isTouch){
22218                 
22219                 var bullets = {
22220                     cls : 'carousel-bullets',
22221                     cn : []
22222                 };
22223                
22224                 if(this.bullets_cls){
22225                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22226                 }
22227                 
22228                 bullets.cn.push({
22229                     cls : 'clear'
22230                 });
22231                 
22232                 cfg.cn[0].cn.push(bullets);
22233             }
22234             
22235             if(this.showarrow){
22236                 cfg.cn[0].cn.push({
22237                     tag : 'div',
22238                     class : 'carousel-arrow',
22239                     cn : [
22240                         {
22241                             tag : 'div',
22242                             class : 'carousel-prev',
22243                             cn : [
22244                                 {
22245                                     tag : 'i',
22246                                     class : 'fa fa-chevron-left'
22247                                 }
22248                             ]
22249                         },
22250                         {
22251                             tag : 'div',
22252                             class : 'carousel-next',
22253                             cn : [
22254                                 {
22255                                     tag : 'i',
22256                                     class : 'fa fa-chevron-right'
22257                                 }
22258                             ]
22259                         }
22260                     ]
22261                 });
22262             }
22263             
22264         }
22265         
22266         return cfg;
22267     },
22268     
22269     initEvents:  function()
22270     {
22271 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22272 //            this.el.on("touchstart", this.onTouchStart, this);
22273 //        }
22274         
22275         if(this.autoslide){
22276             var _this = this;
22277             
22278             this.slideFn = window.setInterval(function() {
22279                 _this.showPanelNext();
22280             }, this.timer);
22281         }
22282         
22283         if(this.showarrow){
22284             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22285             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22286         }
22287         
22288         
22289     },
22290     
22291 //    onTouchStart : function(e, el, o)
22292 //    {
22293 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22294 //            return;
22295 //        }
22296 //        
22297 //        this.showPanelNext();
22298 //    },
22299     
22300     
22301     getChildContainer : function()
22302     {
22303         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22304     },
22305     
22306     /**
22307     * register a Navigation item
22308     * @param {Roo.bootstrap.nav.Item} the navitem to add
22309     */
22310     register : function(item)
22311     {
22312         this.tabs.push( item);
22313         item.navId = this.navId; // not really needed..
22314         this.addBullet();
22315     
22316     },
22317     
22318     getActivePanel : function()
22319     {
22320         var r = false;
22321         Roo.each(this.tabs, function(t) {
22322             if (t.active) {
22323                 r = t;
22324                 return false;
22325             }
22326             return null;
22327         });
22328         return r;
22329         
22330     },
22331     getPanelByName : function(n)
22332     {
22333         var r = false;
22334         Roo.each(this.tabs, function(t) {
22335             if (t.tabId == n) {
22336                 r = t;
22337                 return false;
22338             }
22339             return null;
22340         });
22341         return r;
22342     },
22343     indexOfPanel : function(p)
22344     {
22345         var r = false;
22346         Roo.each(this.tabs, function(t,i) {
22347             if (t.tabId == p.tabId) {
22348                 r = i;
22349                 return false;
22350             }
22351             return null;
22352         });
22353         return r;
22354     },
22355     /**
22356      * show a specific panel
22357      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22358      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22359      */
22360     showPanel : function (pan)
22361     {
22362         if(this.transition || typeof(pan) == 'undefined'){
22363             Roo.log("waiting for the transitionend");
22364             return false;
22365         }
22366         
22367         if (typeof(pan) == 'number') {
22368             pan = this.tabs[pan];
22369         }
22370         
22371         if (typeof(pan) == 'string') {
22372             pan = this.getPanelByName(pan);
22373         }
22374         
22375         var cur = this.getActivePanel();
22376         
22377         if(!pan || !cur){
22378             Roo.log('pan or acitve pan is undefined');
22379             return false;
22380         }
22381         
22382         if (pan.tabId == this.getActivePanel().tabId) {
22383             return true;
22384         }
22385         
22386         if (false === cur.fireEvent('beforedeactivate')) {
22387             return false;
22388         }
22389         
22390         if(this.bullets > 0 && !Roo.isTouch){
22391             this.setActiveBullet(this.indexOfPanel(pan));
22392         }
22393         
22394         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22395             
22396             //class="carousel-item carousel-item-next carousel-item-left"
22397             
22398             this.transition = true;
22399             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22400             var lr = dir == 'next' ? 'left' : 'right';
22401             pan.el.addClass(dir); // or prev
22402             pan.el.addClass('carousel-item-' + dir); // or prev
22403             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22404             cur.el.addClass(lr); // or right
22405             pan.el.addClass(lr);
22406             cur.el.addClass('carousel-item-' +lr); // or right
22407             pan.el.addClass('carousel-item-' +lr);
22408             
22409             
22410             var _this = this;
22411             cur.el.on('transitionend', function() {
22412                 Roo.log("trans end?");
22413                 
22414                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22415                 pan.setActive(true);
22416                 
22417                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22418                 cur.setActive(false);
22419                 
22420                 _this.transition = false;
22421                 
22422             }, this, { single:  true } );
22423             
22424             return true;
22425         }
22426         
22427         cur.setActive(false);
22428         pan.setActive(true);
22429         
22430         return true;
22431         
22432     },
22433     showPanelNext : function()
22434     {
22435         var i = this.indexOfPanel(this.getActivePanel());
22436         
22437         if (i >= this.tabs.length - 1 && !this.autoslide) {
22438             return;
22439         }
22440         
22441         if (i >= this.tabs.length - 1 && this.autoslide) {
22442             i = -1;
22443         }
22444         
22445         this.showPanel(this.tabs[i+1]);
22446     },
22447     
22448     showPanelPrev : function()
22449     {
22450         var i = this.indexOfPanel(this.getActivePanel());
22451         
22452         if (i  < 1 && !this.autoslide) {
22453             return;
22454         }
22455         
22456         if (i < 1 && this.autoslide) {
22457             i = this.tabs.length;
22458         }
22459         
22460         this.showPanel(this.tabs[i-1]);
22461     },
22462     
22463     
22464     addBullet: function()
22465     {
22466         if(!this.bullets || Roo.isTouch){
22467             return;
22468         }
22469         var ctr = this.el.select('.carousel-bullets',true).first();
22470         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22471         var bullet = ctr.createChild({
22472             cls : 'bullet bullet-' + i
22473         },ctr.dom.lastChild);
22474         
22475         
22476         var _this = this;
22477         
22478         bullet.on('click', (function(e, el, o, ii, t){
22479
22480             e.preventDefault();
22481
22482             this.showPanel(ii);
22483
22484             if(this.autoslide && this.slideFn){
22485                 clearInterval(this.slideFn);
22486                 this.slideFn = window.setInterval(function() {
22487                     _this.showPanelNext();
22488                 }, this.timer);
22489             }
22490
22491         }).createDelegate(this, [i, bullet], true));
22492                 
22493         
22494     },
22495      
22496     setActiveBullet : function(i)
22497     {
22498         if(Roo.isTouch){
22499             return;
22500         }
22501         
22502         Roo.each(this.el.select('.bullet', true).elements, function(el){
22503             el.removeClass('selected');
22504         });
22505
22506         var bullet = this.el.select('.bullet-' + i, true).first();
22507         
22508         if(!bullet){
22509             return;
22510         }
22511         
22512         bullet.addClass('selected');
22513     }
22514     
22515     
22516   
22517 });
22518
22519  
22520
22521  
22522  
22523 Roo.apply(Roo.bootstrap.TabGroup, {
22524     
22525     groups: {},
22526      /**
22527     * register a Navigation Group
22528     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22529     */
22530     register : function(navgrp)
22531     {
22532         this.groups[navgrp.navId] = navgrp;
22533         
22534     },
22535     /**
22536     * fetch a Navigation Group based on the navigation ID
22537     * if one does not exist , it will get created.
22538     * @param {string} the navgroup to add
22539     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22540     */
22541     get: function(navId) {
22542         if (typeof(this.groups[navId]) == 'undefined') {
22543             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22544         }
22545         return this.groups[navId] ;
22546     }
22547     
22548     
22549     
22550 });
22551
22552  /*
22553  * - LGPL
22554  *
22555  * TabPanel
22556  * 
22557  */
22558
22559 /**
22560  * @class Roo.bootstrap.TabPanel
22561  * @extends Roo.bootstrap.Component
22562  * @children Roo.bootstrap.Component
22563  * Bootstrap TabPanel class
22564  * @cfg {Boolean} active panel active
22565  * @cfg {String} html panel content
22566  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22567  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22568  * @cfg {String} href click to link..
22569  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22570  * 
22571  * 
22572  * @constructor
22573  * Create a new TabPanel
22574  * @param {Object} config The config object
22575  */
22576
22577 Roo.bootstrap.TabPanel = function(config){
22578     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22579     this.addEvents({
22580         /**
22581              * @event changed
22582              * Fires when the active status changes
22583              * @param {Roo.bootstrap.TabPanel} this
22584              * @param {Boolean} state the new state
22585             
22586          */
22587         'changed': true,
22588         /**
22589              * @event beforedeactivate
22590              * Fires before a tab is de-activated - can be used to do validation on a form.
22591              * @param {Roo.bootstrap.TabPanel} this
22592              * @return {Boolean} false if there is an error
22593             
22594          */
22595         'beforedeactivate': true
22596      });
22597     
22598     this.tabId = this.tabId || Roo.id();
22599   
22600 };
22601
22602 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22603     
22604     active: false,
22605     html: false,
22606     tabId: false,
22607     navId : false,
22608     href : '',
22609     touchSlide : false,
22610     getAutoCreate : function(){
22611         
22612         
22613         var cfg = {
22614             tag: 'div',
22615             // item is needed for carousel - not sure if it has any effect otherwise
22616             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22617             html: this.html || ''
22618         };
22619         
22620         if(this.active){
22621             cfg.cls += ' active';
22622         }
22623         
22624         if(this.tabId){
22625             cfg.tabId = this.tabId;
22626         }
22627         
22628         
22629         
22630         return cfg;
22631     },
22632     
22633     initEvents:  function()
22634     {
22635         var p = this.parent();
22636         
22637         this.navId = this.navId || p.navId;
22638         
22639         if (typeof(this.navId) != 'undefined') {
22640             // not really needed.. but just in case.. parent should be a NavGroup.
22641             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22642             
22643             tg.register(this);
22644             
22645             var i = tg.tabs.length - 1;
22646             
22647             if(this.active && tg.bullets > 0 && i < tg.bullets){
22648                 tg.setActiveBullet(i);
22649             }
22650         }
22651         
22652         this.el.on('click', this.onClick, this);
22653         
22654         if(Roo.isTouch && this.touchSlide){
22655             this.el.on("touchstart", this.onTouchStart, this);
22656             this.el.on("touchmove", this.onTouchMove, this);
22657             this.el.on("touchend", this.onTouchEnd, this);
22658         }
22659         
22660     },
22661     
22662     onRender : function(ct, position)
22663     {
22664         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22665     },
22666     
22667     setActive : function(state)
22668     {
22669         Roo.log("panel - set active " + this.tabId + "=" + state);
22670         
22671         this.active = state;
22672         if (!state) {
22673             this.el.removeClass('active');
22674             
22675         } else  if (!this.el.hasClass('active')) {
22676             this.el.addClass('active');
22677         }
22678         
22679         this.fireEvent('changed', this, state);
22680     },
22681     
22682     onClick : function(e)
22683     {
22684         e.preventDefault();
22685         
22686         if(!this.href.length){
22687             return;
22688         }
22689         
22690         window.location.href = this.href;
22691     },
22692     
22693     startX : 0,
22694     startY : 0,
22695     endX : 0,
22696     endY : 0,
22697     swiping : false,
22698     
22699     onTouchStart : function(e)
22700     {
22701         this.swiping = false;
22702         
22703         this.startX = e.browserEvent.touches[0].clientX;
22704         this.startY = e.browserEvent.touches[0].clientY;
22705     },
22706     
22707     onTouchMove : function(e)
22708     {
22709         this.swiping = true;
22710         
22711         this.endX = e.browserEvent.touches[0].clientX;
22712         this.endY = e.browserEvent.touches[0].clientY;
22713     },
22714     
22715     onTouchEnd : function(e)
22716     {
22717         if(!this.swiping){
22718             this.onClick(e);
22719             return;
22720         }
22721         
22722         var tabGroup = this.parent();
22723         
22724         if(this.endX > this.startX){ // swiping right
22725             tabGroup.showPanelPrev();
22726             return;
22727         }
22728         
22729         if(this.startX > this.endX){ // swiping left
22730             tabGroup.showPanelNext();
22731             return;
22732         }
22733     }
22734     
22735     
22736 });
22737  
22738
22739  
22740
22741  /*
22742  * - LGPL
22743  *
22744  * DateField
22745  * 
22746  */
22747
22748 /**
22749  * @class Roo.bootstrap.DateField
22750  * @extends Roo.bootstrap.Input
22751  * Bootstrap DateField class
22752  * @cfg {Number} weekStart default 0
22753  * @cfg {String} viewMode default empty, (months|years)
22754  * @cfg {String} minViewMode default empty, (months|years)
22755  * @cfg {Number} startDate default -Infinity
22756  * @cfg {Number} endDate default Infinity
22757  * @cfg {Boolean} todayHighlight default false
22758  * @cfg {Boolean} todayBtn default false
22759  * @cfg {Boolean} calendarWeeks default false
22760  * @cfg {Object} daysOfWeekDisabled default empty
22761  * @cfg {Boolean} singleMode default false (true | false)
22762  * 
22763  * @cfg {Boolean} keyboardNavigation default true
22764  * @cfg {String} language default en
22765  * 
22766  * @constructor
22767  * Create a new DateField
22768  * @param {Object} config The config object
22769  */
22770
22771 Roo.bootstrap.DateField = function(config){
22772     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22773      this.addEvents({
22774             /**
22775              * @event show
22776              * Fires when this field show.
22777              * @param {Roo.bootstrap.DateField} this
22778              * @param {Mixed} date The date value
22779              */
22780             show : true,
22781             /**
22782              * @event show
22783              * Fires when this field hide.
22784              * @param {Roo.bootstrap.DateField} this
22785              * @param {Mixed} date The date value
22786              */
22787             hide : true,
22788             /**
22789              * @event select
22790              * Fires when select a date.
22791              * @param {Roo.bootstrap.DateField} this
22792              * @param {Mixed} date The date value
22793              */
22794             select : true,
22795             /**
22796              * @event beforeselect
22797              * Fires when before select a date.
22798              * @param {Roo.bootstrap.DateField} this
22799              * @param {Mixed} date The date value
22800              */
22801             beforeselect : true
22802         });
22803 };
22804
22805 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22806     
22807     /**
22808      * @cfg {String} format
22809      * The default date format string which can be overriden for localization support.  The format must be
22810      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22811      */
22812     format : "m/d/y",
22813     /**
22814      * @cfg {String} altFormats
22815      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22816      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22817      */
22818     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22819     
22820     weekStart : 0,
22821     
22822     viewMode : '',
22823     
22824     minViewMode : '',
22825     
22826     todayHighlight : false,
22827     
22828     todayBtn: false,
22829     
22830     language: 'en',
22831     
22832     keyboardNavigation: true,
22833     
22834     calendarWeeks: false,
22835     
22836     startDate: -Infinity,
22837     
22838     endDate: Infinity,
22839     
22840     daysOfWeekDisabled: [],
22841     
22842     _events: [],
22843     
22844     singleMode : false,
22845     
22846     UTCDate: function()
22847     {
22848         return new Date(Date.UTC.apply(Date, arguments));
22849     },
22850     
22851     UTCToday: function()
22852     {
22853         var today = new Date();
22854         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22855     },
22856     
22857     getDate: function() {
22858             var d = this.getUTCDate();
22859             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22860     },
22861     
22862     getUTCDate: function() {
22863             return this.date;
22864     },
22865     
22866     setDate: function(d) {
22867             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22868     },
22869     
22870     setUTCDate: function(d) {
22871             this.date = d;
22872             this.setValue(this.formatDate(this.date));
22873     },
22874         
22875     onRender: function(ct, position)
22876     {
22877         
22878         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22879         
22880         this.language = this.language || 'en';
22881         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22882         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22883         
22884         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22885         this.format = this.format || 'm/d/y';
22886         this.isInline = false;
22887         this.isInput = true;
22888         this.component = this.el.select('.add-on', true).first() || false;
22889         this.component = (this.component && this.component.length === 0) ? false : this.component;
22890         this.hasInput = this.component && this.inputEl().length;
22891         
22892         if (typeof(this.minViewMode === 'string')) {
22893             switch (this.minViewMode) {
22894                 case 'months':
22895                     this.minViewMode = 1;
22896                     break;
22897                 case 'years':
22898                     this.minViewMode = 2;
22899                     break;
22900                 default:
22901                     this.minViewMode = 0;
22902                     break;
22903             }
22904         }
22905         
22906         if (typeof(this.viewMode === 'string')) {
22907             switch (this.viewMode) {
22908                 case 'months':
22909                     this.viewMode = 1;
22910                     break;
22911                 case 'years':
22912                     this.viewMode = 2;
22913                     break;
22914                 default:
22915                     this.viewMode = 0;
22916                     break;
22917             }
22918         }
22919                 
22920         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22921         
22922 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22923         
22924         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22925         
22926         this.picker().on('mousedown', this.onMousedown, this);
22927         this.picker().on('click', this.onClick, this);
22928         
22929         this.picker().addClass('datepicker-dropdown');
22930         
22931         this.startViewMode = this.viewMode;
22932         
22933         if(this.singleMode){
22934             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22935                 v.setVisibilityMode(Roo.Element.DISPLAY);
22936                 v.hide();
22937             });
22938             
22939             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22940                 v.setStyle('width', '189px');
22941             });
22942         }
22943         
22944         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22945             if(!this.calendarWeeks){
22946                 v.remove();
22947                 return;
22948             }
22949             
22950             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22951             v.attr('colspan', function(i, val){
22952                 return parseInt(val) + 1;
22953             });
22954         });
22955                         
22956         
22957         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22958         
22959         this.setStartDate(this.startDate);
22960         this.setEndDate(this.endDate);
22961         
22962         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22963         
22964         this.fillDow();
22965         this.fillMonths();
22966         this.update();
22967         this.showMode();
22968         
22969         if(this.isInline) {
22970             this.showPopup();
22971         }
22972     },
22973     
22974     picker : function()
22975     {
22976         return this.pickerEl;
22977 //        return this.el.select('.datepicker', true).first();
22978     },
22979     
22980     fillDow: function()
22981     {
22982         var dowCnt = this.weekStart;
22983         
22984         var dow = {
22985             tag: 'tr',
22986             cn: [
22987                 
22988             ]
22989         };
22990         
22991         if(this.calendarWeeks){
22992             dow.cn.push({
22993                 tag: 'th',
22994                 cls: 'cw',
22995                 html: '&nbsp;'
22996             })
22997         }
22998         
22999         while (dowCnt < this.weekStart + 7) {
23000             dow.cn.push({
23001                 tag: 'th',
23002                 cls: 'dow',
23003                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23004             });
23005         }
23006         
23007         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23008     },
23009     
23010     fillMonths: function()
23011     {    
23012         var i = 0;
23013         var months = this.picker().select('>.datepicker-months td', true).first();
23014         
23015         months.dom.innerHTML = '';
23016         
23017         while (i < 12) {
23018             var month = {
23019                 tag: 'span',
23020                 cls: 'month',
23021                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
23022             };
23023             
23024             months.createChild(month);
23025         }
23026         
23027     },
23028     
23029     update: function()
23030     {
23031         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;
23032         
23033         if (this.date < this.startDate) {
23034             this.viewDate = new Date(this.startDate);
23035         } else if (this.date > this.endDate) {
23036             this.viewDate = new Date(this.endDate);
23037         } else {
23038             this.viewDate = new Date(this.date);
23039         }
23040         
23041         this.fill();
23042     },
23043     
23044     fill: function() 
23045     {
23046         var d = new Date(this.viewDate),
23047                 year = d.getUTCFullYear(),
23048                 month = d.getUTCMonth(),
23049                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23050                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23051                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23052                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23053                 currentDate = this.date && this.date.valueOf(),
23054                 today = this.UTCToday();
23055         
23056         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
23057         
23058 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
23059         
23060 //        this.picker.select('>tfoot th.today').
23061 //                                              .text(dates[this.language].today)
23062 //                                              .toggle(this.todayBtn !== false);
23063     
23064         this.updateNavArrows();
23065         this.fillMonths();
23066                                                 
23067         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23068         
23069         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23070          
23071         prevMonth.setUTCDate(day);
23072         
23073         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23074         
23075         var nextMonth = new Date(prevMonth);
23076         
23077         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23078         
23079         nextMonth = nextMonth.valueOf();
23080         
23081         var fillMonths = false;
23082         
23083         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23084         
23085         while(prevMonth.valueOf() <= nextMonth) {
23086             var clsName = '';
23087             
23088             if (prevMonth.getUTCDay() === this.weekStart) {
23089                 if(fillMonths){
23090                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23091                 }
23092                     
23093                 fillMonths = {
23094                     tag: 'tr',
23095                     cn: []
23096                 };
23097                 
23098                 if(this.calendarWeeks){
23099                     // ISO 8601: First week contains first thursday.
23100                     // ISO also states week starts on Monday, but we can be more abstract here.
23101                     var
23102                     // Start of current week: based on weekstart/current date
23103                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23104                     // Thursday of this week
23105                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23106                     // First Thursday of year, year from thursday
23107                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23108                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23109                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23110                     
23111                     fillMonths.cn.push({
23112                         tag: 'td',
23113                         cls: 'cw',
23114                         html: calWeek
23115                     });
23116                 }
23117             }
23118             
23119             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23120                 clsName += ' old';
23121             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23122                 clsName += ' new';
23123             }
23124             if (this.todayHighlight &&
23125                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23126                 prevMonth.getUTCMonth() == today.getMonth() &&
23127                 prevMonth.getUTCDate() == today.getDate()) {
23128                 clsName += ' today';
23129             }
23130             
23131             if (currentDate && prevMonth.valueOf() === currentDate) {
23132                 clsName += ' active';
23133             }
23134             
23135             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23136                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23137                     clsName += ' disabled';
23138             }
23139             
23140             fillMonths.cn.push({
23141                 tag: 'td',
23142                 cls: 'day ' + clsName,
23143                 html: prevMonth.getDate()
23144             });
23145             
23146             prevMonth.setDate(prevMonth.getDate()+1);
23147         }
23148           
23149         var currentYear = this.date && this.date.getUTCFullYear();
23150         var currentMonth = this.date && this.date.getUTCMonth();
23151         
23152         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23153         
23154         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23155             v.removeClass('active');
23156             
23157             if(currentYear === year && k === currentMonth){
23158                 v.addClass('active');
23159             }
23160             
23161             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23162                 v.addClass('disabled');
23163             }
23164             
23165         });
23166         
23167         
23168         year = parseInt(year/10, 10) * 10;
23169         
23170         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23171         
23172         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23173         
23174         year -= 1;
23175         for (var i = -1; i < 11; i++) {
23176             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23177                 tag: 'span',
23178                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23179                 html: year
23180             });
23181             
23182             year += 1;
23183         }
23184     },
23185     
23186     showMode: function(dir) 
23187     {
23188         if (dir) {
23189             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23190         }
23191         
23192         Roo.each(this.picker().select('>div',true).elements, function(v){
23193             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23194             v.hide();
23195         });
23196         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
23197     },
23198     
23199     place: function()
23200     {
23201         if(this.isInline) {
23202             return;
23203         }
23204         
23205         this.picker().removeClass(['bottom', 'top']);
23206         
23207         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23208             /*
23209              * place to the top of element!
23210              *
23211              */
23212             
23213             this.picker().addClass('top');
23214             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23215             
23216             return;
23217         }
23218         
23219         this.picker().addClass('bottom');
23220         
23221         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23222     },
23223     
23224     parseDate : function(value)
23225     {
23226         if(!value || value instanceof Date){
23227             return value;
23228         }
23229         var v = Date.parseDate(value, this.format);
23230         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23231             v = Date.parseDate(value, 'Y-m-d');
23232         }
23233         if(!v && this.altFormats){
23234             if(!this.altFormatsArray){
23235                 this.altFormatsArray = this.altFormats.split("|");
23236             }
23237             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23238                 v = Date.parseDate(value, this.altFormatsArray[i]);
23239             }
23240         }
23241         return v;
23242     },
23243     
23244     formatDate : function(date, fmt)
23245     {   
23246         return (!date || !(date instanceof Date)) ?
23247         date : date.dateFormat(fmt || this.format);
23248     },
23249     
23250     onFocus : function()
23251     {
23252         Roo.bootstrap.DateField.superclass.onFocus.call(this);
23253         this.showPopup();
23254     },
23255     
23256     onBlur : function()
23257     {
23258         Roo.bootstrap.DateField.superclass.onBlur.call(this);
23259         
23260         var d = this.inputEl().getValue();
23261         
23262         this.setValue(d);
23263                 
23264         this.hidePopup();
23265     },
23266     
23267     showPopup : function()
23268     {
23269         this.picker().show();
23270         this.update();
23271         this.place();
23272         
23273         this.fireEvent('showpopup', this, this.date);
23274     },
23275     
23276     hidePopup : function()
23277     {
23278         if(this.isInline) {
23279             return;
23280         }
23281         this.picker().hide();
23282         this.viewMode = this.startViewMode;
23283         this.showMode();
23284         
23285         this.fireEvent('hidepopup', this, this.date);
23286         
23287     },
23288     
23289     onMousedown: function(e)
23290     {
23291         e.stopPropagation();
23292         e.preventDefault();
23293     },
23294     
23295     keyup: function(e)
23296     {
23297         Roo.bootstrap.DateField.superclass.keyup.call(this);
23298         this.update();
23299     },
23300
23301     setValue: function(v)
23302     {
23303         if(this.fireEvent('beforeselect', this, v) !== false){
23304             var d = new Date(this.parseDate(v) ).clearTime();
23305         
23306             if(isNaN(d.getTime())){
23307                 this.date = this.viewDate = '';
23308                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23309                 return;
23310             }
23311
23312             v = this.formatDate(d);
23313
23314             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
23315
23316             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23317
23318             this.update();
23319
23320             this.fireEvent('select', this, this.date);
23321         }
23322     },
23323     
23324     getValue: function()
23325     {
23326         return this.formatDate(this.date);
23327     },
23328     
23329     fireKey: function(e)
23330     {
23331         if (!this.picker().isVisible()){
23332             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23333                 this.showPopup();
23334             }
23335             return;
23336         }
23337         
23338         var dateChanged = false,
23339         dir, day, month,
23340         newDate, newViewDate;
23341         
23342         switch(e.keyCode){
23343             case 27: // escape
23344                 this.hidePopup();
23345                 e.preventDefault();
23346                 break;
23347             case 37: // left
23348             case 39: // right
23349                 if (!this.keyboardNavigation) {
23350                     break;
23351                 }
23352                 dir = e.keyCode == 37 ? -1 : 1;
23353                 
23354                 if (e.ctrlKey){
23355                     newDate = this.moveYear(this.date, dir);
23356                     newViewDate = this.moveYear(this.viewDate, dir);
23357                 } else if (e.shiftKey){
23358                     newDate = this.moveMonth(this.date, dir);
23359                     newViewDate = this.moveMonth(this.viewDate, dir);
23360                 } else {
23361                     newDate = new Date(this.date);
23362                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23363                     newViewDate = new Date(this.viewDate);
23364                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23365                 }
23366                 if (this.dateWithinRange(newDate)){
23367                     this.date = newDate;
23368                     this.viewDate = newViewDate;
23369                     this.setValue(this.formatDate(this.date));
23370 //                    this.update();
23371                     e.preventDefault();
23372                     dateChanged = true;
23373                 }
23374                 break;
23375             case 38: // up
23376             case 40: // down
23377                 if (!this.keyboardNavigation) {
23378                     break;
23379                 }
23380                 dir = e.keyCode == 38 ? -1 : 1;
23381                 if (e.ctrlKey){
23382                     newDate = this.moveYear(this.date, dir);
23383                     newViewDate = this.moveYear(this.viewDate, dir);
23384                 } else if (e.shiftKey){
23385                     newDate = this.moveMonth(this.date, dir);
23386                     newViewDate = this.moveMonth(this.viewDate, dir);
23387                 } else {
23388                     newDate = new Date(this.date);
23389                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23390                     newViewDate = new Date(this.viewDate);
23391                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23392                 }
23393                 if (this.dateWithinRange(newDate)){
23394                     this.date = newDate;
23395                     this.viewDate = newViewDate;
23396                     this.setValue(this.formatDate(this.date));
23397 //                    this.update();
23398                     e.preventDefault();
23399                     dateChanged = true;
23400                 }
23401                 break;
23402             case 13: // enter
23403                 this.setValue(this.formatDate(this.date));
23404                 this.hidePopup();
23405                 e.preventDefault();
23406                 break;
23407             case 9: // tab
23408                 this.setValue(this.formatDate(this.date));
23409                 this.hidePopup();
23410                 break;
23411             case 16: // shift
23412             case 17: // ctrl
23413             case 18: // alt
23414                 break;
23415             default :
23416                 this.hidePopup();
23417                 
23418         }
23419     },
23420     
23421     
23422     onClick: function(e) 
23423     {
23424         e.stopPropagation();
23425         e.preventDefault();
23426         
23427         var target = e.getTarget();
23428         
23429         if(target.nodeName.toLowerCase() === 'i'){
23430             target = Roo.get(target).dom.parentNode;
23431         }
23432         
23433         var nodeName = target.nodeName;
23434         var className = target.className;
23435         var html = target.innerHTML;
23436         //Roo.log(nodeName);
23437         
23438         switch(nodeName.toLowerCase()) {
23439             case 'th':
23440                 switch(className) {
23441                     case 'switch':
23442                         this.showMode(1);
23443                         break;
23444                     case 'prev':
23445                     case 'next':
23446                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23447                         switch(this.viewMode){
23448                                 case 0:
23449                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23450                                         break;
23451                                 case 1:
23452                                 case 2:
23453                                         this.viewDate = this.moveYear(this.viewDate, dir);
23454                                         break;
23455                         }
23456                         this.fill();
23457                         break;
23458                     case 'today':
23459                         var date = new Date();
23460                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23461 //                        this.fill()
23462                         this.setValue(this.formatDate(this.date));
23463                         
23464                         this.hidePopup();
23465                         break;
23466                 }
23467                 break;
23468             case 'span':
23469                 if (className.indexOf('disabled') < 0) {
23470                 if (!this.viewDate) {
23471                     this.viewDate = new Date();
23472                 }
23473                 this.viewDate.setUTCDate(1);
23474                     if (className.indexOf('month') > -1) {
23475                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23476                     } else {
23477                         var year = parseInt(html, 10) || 0;
23478                         this.viewDate.setUTCFullYear(year);
23479                         
23480                     }
23481                     
23482                     if(this.singleMode){
23483                         this.setValue(this.formatDate(this.viewDate));
23484                         this.hidePopup();
23485                         return;
23486                     }
23487                     
23488                     this.showMode(-1);
23489                     this.fill();
23490                 }
23491                 break;
23492                 
23493             case 'td':
23494                 //Roo.log(className);
23495                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23496                     var day = parseInt(html, 10) || 1;
23497                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23498                         month = (this.viewDate || new Date()).getUTCMonth();
23499
23500                     if (className.indexOf('old') > -1) {
23501                         if(month === 0 ){
23502                             month = 11;
23503                             year -= 1;
23504                         }else{
23505                             month -= 1;
23506                         }
23507                     } else if (className.indexOf('new') > -1) {
23508                         if (month == 11) {
23509                             month = 0;
23510                             year += 1;
23511                         } else {
23512                             month += 1;
23513                         }
23514                     }
23515                     //Roo.log([year,month,day]);
23516                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23517                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23518 //                    this.fill();
23519                     //Roo.log(this.formatDate(this.date));
23520                     this.setValue(this.formatDate(this.date));
23521                     this.hidePopup();
23522                 }
23523                 break;
23524         }
23525     },
23526     
23527     setStartDate: function(startDate)
23528     {
23529         this.startDate = startDate || -Infinity;
23530         if (this.startDate !== -Infinity) {
23531             this.startDate = this.parseDate(this.startDate);
23532         }
23533         this.update();
23534         this.updateNavArrows();
23535     },
23536
23537     setEndDate: function(endDate)
23538     {
23539         this.endDate = endDate || Infinity;
23540         if (this.endDate !== Infinity) {
23541             this.endDate = this.parseDate(this.endDate);
23542         }
23543         this.update();
23544         this.updateNavArrows();
23545     },
23546     
23547     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23548     {
23549         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23550         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23551             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23552         }
23553         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23554             return parseInt(d, 10);
23555         });
23556         this.update();
23557         this.updateNavArrows();
23558     },
23559     
23560     updateNavArrows: function() 
23561     {
23562         if(this.singleMode){
23563             return;
23564         }
23565         
23566         var d = new Date(this.viewDate),
23567         year = d.getUTCFullYear(),
23568         month = d.getUTCMonth();
23569         
23570         Roo.each(this.picker().select('.prev', true).elements, function(v){
23571             v.show();
23572             switch (this.viewMode) {
23573                 case 0:
23574
23575                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23576                         v.hide();
23577                     }
23578                     break;
23579                 case 1:
23580                 case 2:
23581                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23582                         v.hide();
23583                     }
23584                     break;
23585             }
23586         });
23587         
23588         Roo.each(this.picker().select('.next', true).elements, function(v){
23589             v.show();
23590             switch (this.viewMode) {
23591                 case 0:
23592
23593                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23594                         v.hide();
23595                     }
23596                     break;
23597                 case 1:
23598                 case 2:
23599                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23600                         v.hide();
23601                     }
23602                     break;
23603             }
23604         })
23605     },
23606     
23607     moveMonth: function(date, dir)
23608     {
23609         if (!dir) {
23610             return date;
23611         }
23612         var new_date = new Date(date.valueOf()),
23613         day = new_date.getUTCDate(),
23614         month = new_date.getUTCMonth(),
23615         mag = Math.abs(dir),
23616         new_month, test;
23617         dir = dir > 0 ? 1 : -1;
23618         if (mag == 1){
23619             test = dir == -1
23620             // If going back one month, make sure month is not current month
23621             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23622             ? function(){
23623                 return new_date.getUTCMonth() == month;
23624             }
23625             // If going forward one month, make sure month is as expected
23626             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23627             : function(){
23628                 return new_date.getUTCMonth() != new_month;
23629             };
23630             new_month = month + dir;
23631             new_date.setUTCMonth(new_month);
23632             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23633             if (new_month < 0 || new_month > 11) {
23634                 new_month = (new_month + 12) % 12;
23635             }
23636         } else {
23637             // For magnitudes >1, move one month at a time...
23638             for (var i=0; i<mag; i++) {
23639                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23640                 new_date = this.moveMonth(new_date, dir);
23641             }
23642             // ...then reset the day, keeping it in the new month
23643             new_month = new_date.getUTCMonth();
23644             new_date.setUTCDate(day);
23645             test = function(){
23646                 return new_month != new_date.getUTCMonth();
23647             };
23648         }
23649         // Common date-resetting loop -- if date is beyond end of month, make it
23650         // end of month
23651         while (test()){
23652             new_date.setUTCDate(--day);
23653             new_date.setUTCMonth(new_month);
23654         }
23655         return new_date;
23656     },
23657
23658     moveYear: function(date, dir)
23659     {
23660         return this.moveMonth(date, dir*12);
23661     },
23662
23663     dateWithinRange: function(date)
23664     {
23665         return date >= this.startDate && date <= this.endDate;
23666     },
23667
23668     
23669     remove: function() 
23670     {
23671         this.picker().remove();
23672     },
23673     
23674     validateValue : function(value)
23675     {
23676         if(this.getVisibilityEl().hasClass('hidden')){
23677             return true;
23678         }
23679         
23680         if(value.length < 1)  {
23681             if(this.allowBlank){
23682                 return true;
23683             }
23684             return false;
23685         }
23686         
23687         if(value.length < this.minLength){
23688             return false;
23689         }
23690         if(value.length > this.maxLength){
23691             return false;
23692         }
23693         if(this.vtype){
23694             var vt = Roo.form.VTypes;
23695             if(!vt[this.vtype](value, this)){
23696                 return false;
23697             }
23698         }
23699         if(typeof this.validator == "function"){
23700             var msg = this.validator(value);
23701             if(msg !== true){
23702                 return false;
23703             }
23704         }
23705         
23706         if(this.regex && !this.regex.test(value)){
23707             return false;
23708         }
23709         
23710         if(typeof(this.parseDate(value)) == 'undefined'){
23711             return false;
23712         }
23713         
23714         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23715             return false;
23716         }      
23717         
23718         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23719             return false;
23720         } 
23721         
23722         
23723         return true;
23724     },
23725     
23726     reset : function()
23727     {
23728         this.date = this.viewDate = '';
23729         
23730         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23731     }
23732    
23733 });
23734
23735 Roo.apply(Roo.bootstrap.DateField,  {
23736     
23737     head : {
23738         tag: 'thead',
23739         cn: [
23740         {
23741             tag: 'tr',
23742             cn: [
23743             {
23744                 tag: 'th',
23745                 cls: 'prev',
23746                 html: '<i class="fa fa-arrow-left"/>'
23747             },
23748             {
23749                 tag: 'th',
23750                 cls: 'switch',
23751                 colspan: '5'
23752             },
23753             {
23754                 tag: 'th',
23755                 cls: 'next',
23756                 html: '<i class="fa fa-arrow-right"/>'
23757             }
23758
23759             ]
23760         }
23761         ]
23762     },
23763     
23764     content : {
23765         tag: 'tbody',
23766         cn: [
23767         {
23768             tag: 'tr',
23769             cn: [
23770             {
23771                 tag: 'td',
23772                 colspan: '7'
23773             }
23774             ]
23775         }
23776         ]
23777     },
23778     
23779     footer : {
23780         tag: 'tfoot',
23781         cn: [
23782         {
23783             tag: 'tr',
23784             cn: [
23785             {
23786                 tag: 'th',
23787                 colspan: '7',
23788                 cls: 'today'
23789             }
23790                     
23791             ]
23792         }
23793         ]
23794     },
23795     
23796     dates:{
23797         en: {
23798             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23799             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23800             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23801             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23802             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23803             today: "Today"
23804         }
23805     },
23806     
23807     modes: [
23808     {
23809         clsName: 'days',
23810         navFnc: 'Month',
23811         navStep: 1
23812     },
23813     {
23814         clsName: 'months',
23815         navFnc: 'FullYear',
23816         navStep: 1
23817     },
23818     {
23819         clsName: 'years',
23820         navFnc: 'FullYear',
23821         navStep: 10
23822     }]
23823 });
23824
23825 Roo.apply(Roo.bootstrap.DateField,  {
23826   
23827     template : {
23828         tag: 'div',
23829         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23830         cn: [
23831         {
23832             tag: 'div',
23833             cls: 'datepicker-days',
23834             cn: [
23835             {
23836                 tag: 'table',
23837                 cls: 'table-condensed',
23838                 cn:[
23839                 Roo.bootstrap.DateField.head,
23840                 {
23841                     tag: 'tbody'
23842                 },
23843                 Roo.bootstrap.DateField.footer
23844                 ]
23845             }
23846             ]
23847         },
23848         {
23849             tag: 'div',
23850             cls: 'datepicker-months',
23851             cn: [
23852             {
23853                 tag: 'table',
23854                 cls: 'table-condensed',
23855                 cn:[
23856                 Roo.bootstrap.DateField.head,
23857                 Roo.bootstrap.DateField.content,
23858                 Roo.bootstrap.DateField.footer
23859                 ]
23860             }
23861             ]
23862         },
23863         {
23864             tag: 'div',
23865             cls: 'datepicker-years',
23866             cn: [
23867             {
23868                 tag: 'table',
23869                 cls: 'table-condensed',
23870                 cn:[
23871                 Roo.bootstrap.DateField.head,
23872                 Roo.bootstrap.DateField.content,
23873                 Roo.bootstrap.DateField.footer
23874                 ]
23875             }
23876             ]
23877         }
23878         ]
23879     }
23880 });
23881
23882  
23883
23884  /*
23885  * - LGPL
23886  *
23887  * TimeField
23888  * 
23889  */
23890
23891 /**
23892  * @class Roo.bootstrap.TimeField
23893  * @extends Roo.bootstrap.Input
23894  * Bootstrap DateField class
23895  * 
23896  * 
23897  * @constructor
23898  * Create a new TimeField
23899  * @param {Object} config The config object
23900  */
23901
23902 Roo.bootstrap.TimeField = function(config){
23903     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23904     this.addEvents({
23905             /**
23906              * @event show
23907              * Fires when this field show.
23908              * @param {Roo.bootstrap.DateField} thisthis
23909              * @param {Mixed} date The date value
23910              */
23911             show : true,
23912             /**
23913              * @event show
23914              * Fires when this field hide.
23915              * @param {Roo.bootstrap.DateField} this
23916              * @param {Mixed} date The date value
23917              */
23918             hide : true,
23919             /**
23920              * @event select
23921              * Fires when select a date.
23922              * @param {Roo.bootstrap.DateField} this
23923              * @param {Mixed} date The date value
23924              */
23925             select : true
23926         });
23927 };
23928
23929 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23930     
23931     /**
23932      * @cfg {String} format
23933      * The default time format string which can be overriden for localization support.  The format must be
23934      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23935      */
23936     format : "H:i",
23937
23938     getAutoCreate : function()
23939     {
23940         this.after = '<i class="fa far fa-clock"></i>';
23941         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23942         
23943          
23944     },
23945     onRender: function(ct, position)
23946     {
23947         
23948         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23949                 
23950         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23951         
23952         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23953         
23954         this.pop = this.picker().select('>.datepicker-time',true).first();
23955         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23956         
23957         this.picker().on('mousedown', this.onMousedown, this);
23958         this.picker().on('click', this.onClick, this);
23959         
23960         this.picker().addClass('datepicker-dropdown');
23961     
23962         this.fillTime();
23963         this.update();
23964             
23965         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23966         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23967         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23968         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23969         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23970         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23971
23972     },
23973     
23974     fireKey: function(e){
23975         if (!this.picker().isVisible()){
23976             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23977                 this.show();
23978             }
23979             return;
23980         }
23981
23982         e.preventDefault();
23983         
23984         switch(e.keyCode){
23985             case 27: // escape
23986                 this.hide();
23987                 break;
23988             case 37: // left
23989             case 39: // right
23990                 this.onTogglePeriod();
23991                 break;
23992             case 38: // up
23993                 this.onIncrementMinutes();
23994                 break;
23995             case 40: // down
23996                 this.onDecrementMinutes();
23997                 break;
23998             case 13: // enter
23999             case 9: // tab
24000                 this.setTime();
24001                 break;
24002         }
24003     },
24004     
24005     onClick: function(e) {
24006         e.stopPropagation();
24007         e.preventDefault();
24008     },
24009     
24010     picker : function()
24011     {
24012         return this.pickerEl;
24013     },
24014     
24015     fillTime: function()
24016     {    
24017         var time = this.pop.select('tbody', true).first();
24018         
24019         time.dom.innerHTML = '';
24020         
24021         time.createChild({
24022             tag: 'tr',
24023             cn: [
24024                 {
24025                     tag: 'td',
24026                     cn: [
24027                         {
24028                             tag: 'a',
24029                             href: '#',
24030                             cls: 'btn',
24031                             cn: [
24032                                 {
24033                                     tag: 'i',
24034                                     cls: 'hours-up fa fas fa-chevron-up'
24035                                 }
24036                             ]
24037                         } 
24038                     ]
24039                 },
24040                 {
24041                     tag: 'td',
24042                     cls: 'separator'
24043                 },
24044                 {
24045                     tag: 'td',
24046                     cn: [
24047                         {
24048                             tag: 'a',
24049                             href: '#',
24050                             cls: 'btn',
24051                             cn: [
24052                                 {
24053                                     tag: 'i',
24054                                     cls: 'minutes-up fa fas fa-chevron-up'
24055                                 }
24056                             ]
24057                         }
24058                     ]
24059                 },
24060                 {
24061                     tag: 'td',
24062                     cls: 'separator'
24063                 }
24064             ]
24065         });
24066         
24067         time.createChild({
24068             tag: 'tr',
24069             cn: [
24070                 {
24071                     tag: 'td',
24072                     cn: [
24073                         {
24074                             tag: 'span',
24075                             cls: 'timepicker-hour',
24076                             html: '00'
24077                         }  
24078                     ]
24079                 },
24080                 {
24081                     tag: 'td',
24082                     cls: 'separator',
24083                     html: ':'
24084                 },
24085                 {
24086                     tag: 'td',
24087                     cn: [
24088                         {
24089                             tag: 'span',
24090                             cls: 'timepicker-minute',
24091                             html: '00'
24092                         }  
24093                     ]
24094                 },
24095                 {
24096                     tag: 'td',
24097                     cls: 'separator'
24098                 },
24099                 {
24100                     tag: 'td',
24101                     cn: [
24102                         {
24103                             tag: 'button',
24104                             type: 'button',
24105                             cls: 'btn btn-primary period',
24106                             html: 'AM'
24107                             
24108                         }
24109                     ]
24110                 }
24111             ]
24112         });
24113         
24114         time.createChild({
24115             tag: 'tr',
24116             cn: [
24117                 {
24118                     tag: 'td',
24119                     cn: [
24120                         {
24121                             tag: 'a',
24122                             href: '#',
24123                             cls: 'btn',
24124                             cn: [
24125                                 {
24126                                     tag: 'span',
24127                                     cls: 'hours-down fa fas fa-chevron-down'
24128                                 }
24129                             ]
24130                         }
24131                     ]
24132                 },
24133                 {
24134                     tag: 'td',
24135                     cls: 'separator'
24136                 },
24137                 {
24138                     tag: 'td',
24139                     cn: [
24140                         {
24141                             tag: 'a',
24142                             href: '#',
24143                             cls: 'btn',
24144                             cn: [
24145                                 {
24146                                     tag: 'span',
24147                                     cls: 'minutes-down fa fas fa-chevron-down'
24148                                 }
24149                             ]
24150                         }
24151                     ]
24152                 },
24153                 {
24154                     tag: 'td',
24155                     cls: 'separator'
24156                 }
24157             ]
24158         });
24159         
24160     },
24161     
24162     update: function()
24163     {
24164         
24165         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24166         
24167         this.fill();
24168     },
24169     
24170     fill: function() 
24171     {
24172         var hours = this.time.getHours();
24173         var minutes = this.time.getMinutes();
24174         var period = 'AM';
24175         
24176         if(hours > 11){
24177             period = 'PM';
24178         }
24179         
24180         if(hours == 0){
24181             hours = 12;
24182         }
24183         
24184         
24185         if(hours > 12){
24186             hours = hours - 12;
24187         }
24188         
24189         if(hours < 10){
24190             hours = '0' + hours;
24191         }
24192         
24193         if(minutes < 10){
24194             minutes = '0' + minutes;
24195         }
24196         
24197         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24198         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24199         this.pop.select('button', true).first().dom.innerHTML = period;
24200         
24201     },
24202     
24203     place: function()
24204     {   
24205         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24206         
24207         var cls = ['bottom'];
24208         
24209         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24210             cls.pop();
24211             cls.push('top');
24212         }
24213         
24214         cls.push('right');
24215         
24216         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24217             cls.pop();
24218             cls.push('left');
24219         }
24220         //this.picker().setXY(20000,20000);
24221         this.picker().addClass(cls.join('-'));
24222         
24223         var _this = this;
24224         
24225         Roo.each(cls, function(c){
24226             if(c == 'bottom'){
24227                 (function() {
24228                  //  
24229                 }).defer(200);
24230                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24231                 //_this.picker().setTop(_this.inputEl().getHeight());
24232                 return;
24233             }
24234             if(c == 'top'){
24235                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24236                 
24237                 //_this.picker().setTop(0 - _this.picker().getHeight());
24238                 return;
24239             }
24240             /*
24241             if(c == 'left'){
24242                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24243                 return;
24244             }
24245             if(c == 'right'){
24246                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24247                 return;
24248             }
24249             */
24250         });
24251         
24252     },
24253   
24254     onFocus : function()
24255     {
24256         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
24257         this.show();
24258     },
24259     
24260     onBlur : function()
24261     {
24262         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
24263         this.hide();
24264     },
24265     
24266     show : function()
24267     {
24268         this.picker().show();
24269         this.pop.show();
24270         this.update();
24271         this.place();
24272         
24273         this.fireEvent('show', this, this.date);
24274     },
24275     
24276     hide : function()
24277     {
24278         this.picker().hide();
24279         this.pop.hide();
24280         
24281         this.fireEvent('hide', this, this.date);
24282     },
24283     
24284     setTime : function()
24285     {
24286         this.hide();
24287         this.setValue(this.time.format(this.format));
24288         
24289         this.fireEvent('select', this, this.date);
24290         
24291         
24292     },
24293     
24294     onMousedown: function(e){
24295         e.stopPropagation();
24296         e.preventDefault();
24297     },
24298     
24299     onIncrementHours: function()
24300     {
24301         Roo.log('onIncrementHours');
24302         this.time = this.time.add(Date.HOUR, 1);
24303         this.update();
24304         
24305     },
24306     
24307     onDecrementHours: function()
24308     {
24309         Roo.log('onDecrementHours');
24310         this.time = this.time.add(Date.HOUR, -1);
24311         this.update();
24312     },
24313     
24314     onIncrementMinutes: function()
24315     {
24316         Roo.log('onIncrementMinutes');
24317         this.time = this.time.add(Date.MINUTE, 1);
24318         this.update();
24319     },
24320     
24321     onDecrementMinutes: function()
24322     {
24323         Roo.log('onDecrementMinutes');
24324         this.time = this.time.add(Date.MINUTE, -1);
24325         this.update();
24326     },
24327     
24328     onTogglePeriod: function()
24329     {
24330         Roo.log('onTogglePeriod');
24331         this.time = this.time.add(Date.HOUR, 12);
24332         this.update();
24333     }
24334     
24335    
24336 });
24337  
24338
24339 Roo.apply(Roo.bootstrap.TimeField,  {
24340   
24341     template : {
24342         tag: 'div',
24343         cls: 'datepicker dropdown-menu',
24344         cn: [
24345             {
24346                 tag: 'div',
24347                 cls: 'datepicker-time',
24348                 cn: [
24349                 {
24350                     tag: 'table',
24351                     cls: 'table-condensed',
24352                     cn:[
24353                         {
24354                             tag: 'tbody',
24355                             cn: [
24356                                 {
24357                                     tag: 'tr',
24358                                     cn: [
24359                                     {
24360                                         tag: 'td',
24361                                         colspan: '7'
24362                                     }
24363                                     ]
24364                                 }
24365                             ]
24366                         },
24367                         {
24368                             tag: 'tfoot',
24369                             cn: [
24370                                 {
24371                                     tag: 'tr',
24372                                     cn: [
24373                                     {
24374                                         tag: 'th',
24375                                         colspan: '7',
24376                                         cls: '',
24377                                         cn: [
24378                                             {
24379                                                 tag: 'button',
24380                                                 cls: 'btn btn-info ok',
24381                                                 html: 'OK'
24382                                             }
24383                                         ]
24384                                     }
24385                     
24386                                     ]
24387                                 }
24388                             ]
24389                         }
24390                     ]
24391                 }
24392                 ]
24393             }
24394         ]
24395     }
24396 });
24397
24398  
24399
24400  /*
24401  * - LGPL
24402  *
24403  * MonthField
24404  * 
24405  */
24406
24407 /**
24408  * @class Roo.bootstrap.MonthField
24409  * @extends Roo.bootstrap.Input
24410  * Bootstrap MonthField class
24411  * 
24412  * @cfg {String} language default en
24413  * 
24414  * @constructor
24415  * Create a new MonthField
24416  * @param {Object} config The config object
24417  */
24418
24419 Roo.bootstrap.MonthField = function(config){
24420     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24421     
24422     this.addEvents({
24423         /**
24424          * @event show
24425          * Fires when this field show.
24426          * @param {Roo.bootstrap.MonthField} this
24427          * @param {Mixed} date The date value
24428          */
24429         show : true,
24430         /**
24431          * @event show
24432          * Fires when this field hide.
24433          * @param {Roo.bootstrap.MonthField} this
24434          * @param {Mixed} date The date value
24435          */
24436         hide : true,
24437         /**
24438          * @event select
24439          * Fires when select a date.
24440          * @param {Roo.bootstrap.MonthField} this
24441          * @param {String} oldvalue The old value
24442          * @param {String} newvalue The new value
24443          */
24444         select : true
24445     });
24446 };
24447
24448 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24449     
24450     onRender: function(ct, position)
24451     {
24452         
24453         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24454         
24455         this.language = this.language || 'en';
24456         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24457         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24458         
24459         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24460         this.isInline = false;
24461         this.isInput = true;
24462         this.component = this.el.select('.add-on', true).first() || false;
24463         this.component = (this.component && this.component.length === 0) ? false : this.component;
24464         this.hasInput = this.component && this.inputEL().length;
24465         
24466         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24467         
24468         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24469         
24470         this.picker().on('mousedown', this.onMousedown, this);
24471         this.picker().on('click', this.onClick, this);
24472         
24473         this.picker().addClass('datepicker-dropdown');
24474         
24475         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24476             v.setStyle('width', '189px');
24477         });
24478         
24479         this.fillMonths();
24480         
24481         this.update();
24482         
24483         if(this.isInline) {
24484             this.show();
24485         }
24486         
24487     },
24488     
24489     setValue: function(v, suppressEvent)
24490     {   
24491         var o = this.getValue();
24492         
24493         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24494         
24495         this.update();
24496
24497         if(suppressEvent !== true){
24498             this.fireEvent('select', this, o, v);
24499         }
24500         
24501     },
24502     
24503     getValue: function()
24504     {
24505         return this.value;
24506     },
24507     
24508     onClick: function(e) 
24509     {
24510         e.stopPropagation();
24511         e.preventDefault();
24512         
24513         var target = e.getTarget();
24514         
24515         if(target.nodeName.toLowerCase() === 'i'){
24516             target = Roo.get(target).dom.parentNode;
24517         }
24518         
24519         var nodeName = target.nodeName;
24520         var className = target.className;
24521         var html = target.innerHTML;
24522         
24523         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24524             return;
24525         }
24526         
24527         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24528         
24529         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24530         
24531         this.hide();
24532                         
24533     },
24534     
24535     picker : function()
24536     {
24537         return this.pickerEl;
24538     },
24539     
24540     fillMonths: function()
24541     {    
24542         var i = 0;
24543         var months = this.picker().select('>.datepicker-months td', true).first();
24544         
24545         months.dom.innerHTML = '';
24546         
24547         while (i < 12) {
24548             var month = {
24549                 tag: 'span',
24550                 cls: 'month',
24551                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24552             };
24553             
24554             months.createChild(month);
24555         }
24556         
24557     },
24558     
24559     update: function()
24560     {
24561         var _this = this;
24562         
24563         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24564             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24565         }
24566         
24567         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24568             e.removeClass('active');
24569             
24570             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24571                 e.addClass('active');
24572             }
24573         })
24574     },
24575     
24576     place: function()
24577     {
24578         if(this.isInline) {
24579             return;
24580         }
24581         
24582         this.picker().removeClass(['bottom', 'top']);
24583         
24584         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24585             /*
24586              * place to the top of element!
24587              *
24588              */
24589             
24590             this.picker().addClass('top');
24591             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24592             
24593             return;
24594         }
24595         
24596         this.picker().addClass('bottom');
24597         
24598         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24599     },
24600     
24601     onFocus : function()
24602     {
24603         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24604         this.show();
24605     },
24606     
24607     onBlur : function()
24608     {
24609         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24610         
24611         var d = this.inputEl().getValue();
24612         
24613         this.setValue(d);
24614                 
24615         this.hide();
24616     },
24617     
24618     show : function()
24619     {
24620         this.picker().show();
24621         this.picker().select('>.datepicker-months', true).first().show();
24622         this.update();
24623         this.place();
24624         
24625         this.fireEvent('show', this, this.date);
24626     },
24627     
24628     hide : function()
24629     {
24630         if(this.isInline) {
24631             return;
24632         }
24633         this.picker().hide();
24634         this.fireEvent('hide', this, this.date);
24635         
24636     },
24637     
24638     onMousedown: function(e)
24639     {
24640         e.stopPropagation();
24641         e.preventDefault();
24642     },
24643     
24644     keyup: function(e)
24645     {
24646         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24647         this.update();
24648     },
24649
24650     fireKey: function(e)
24651     {
24652         if (!this.picker().isVisible()){
24653             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24654                 this.show();
24655             }
24656             return;
24657         }
24658         
24659         var dir;
24660         
24661         switch(e.keyCode){
24662             case 27: // escape
24663                 this.hide();
24664                 e.preventDefault();
24665                 break;
24666             case 37: // left
24667             case 39: // right
24668                 dir = e.keyCode == 37 ? -1 : 1;
24669                 
24670                 this.vIndex = this.vIndex + dir;
24671                 
24672                 if(this.vIndex < 0){
24673                     this.vIndex = 0;
24674                 }
24675                 
24676                 if(this.vIndex > 11){
24677                     this.vIndex = 11;
24678                 }
24679                 
24680                 if(isNaN(this.vIndex)){
24681                     this.vIndex = 0;
24682                 }
24683                 
24684                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24685                 
24686                 break;
24687             case 38: // up
24688             case 40: // down
24689                 
24690                 dir = e.keyCode == 38 ? -1 : 1;
24691                 
24692                 this.vIndex = this.vIndex + dir * 4;
24693                 
24694                 if(this.vIndex < 0){
24695                     this.vIndex = 0;
24696                 }
24697                 
24698                 if(this.vIndex > 11){
24699                     this.vIndex = 11;
24700                 }
24701                 
24702                 if(isNaN(this.vIndex)){
24703                     this.vIndex = 0;
24704                 }
24705                 
24706                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24707                 break;
24708                 
24709             case 13: // enter
24710                 
24711                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24712                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24713                 }
24714                 
24715                 this.hide();
24716                 e.preventDefault();
24717                 break;
24718             case 9: // tab
24719                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24720                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24721                 }
24722                 this.hide();
24723                 break;
24724             case 16: // shift
24725             case 17: // ctrl
24726             case 18: // alt
24727                 break;
24728             default :
24729                 this.hide();
24730                 
24731         }
24732     },
24733     
24734     remove: function() 
24735     {
24736         this.picker().remove();
24737     }
24738    
24739 });
24740
24741 Roo.apply(Roo.bootstrap.MonthField,  {
24742     
24743     content : {
24744         tag: 'tbody',
24745         cn: [
24746         {
24747             tag: 'tr',
24748             cn: [
24749             {
24750                 tag: 'td',
24751                 colspan: '7'
24752             }
24753             ]
24754         }
24755         ]
24756     },
24757     
24758     dates:{
24759         en: {
24760             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24761             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24762         }
24763     }
24764 });
24765
24766 Roo.apply(Roo.bootstrap.MonthField,  {
24767   
24768     template : {
24769         tag: 'div',
24770         cls: 'datepicker dropdown-menu roo-dynamic',
24771         cn: [
24772             {
24773                 tag: 'div',
24774                 cls: 'datepicker-months',
24775                 cn: [
24776                 {
24777                     tag: 'table',
24778                     cls: 'table-condensed',
24779                     cn:[
24780                         Roo.bootstrap.DateField.content
24781                     ]
24782                 }
24783                 ]
24784             }
24785         ]
24786     }
24787 });
24788
24789  
24790
24791  
24792  /*
24793  * - LGPL
24794  *
24795  * CheckBox
24796  * 
24797  */
24798
24799 /**
24800  * @class Roo.bootstrap.CheckBox
24801  * @extends Roo.bootstrap.Input
24802  * Bootstrap CheckBox class
24803  * 
24804  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24805  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24806  * @cfg {String} boxLabel The text that appears beside the checkbox
24807  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24808  * @cfg {Boolean} checked initnal the element
24809  * @cfg {Boolean} inline inline the element (default false)
24810  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24811  * @cfg {String} tooltip label tooltip
24812  * 
24813  * @constructor
24814  * Create a new CheckBox
24815  * @param {Object} config The config object
24816  */
24817
24818 Roo.bootstrap.CheckBox = function(config){
24819     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24820    
24821     this.addEvents({
24822         /**
24823         * @event check
24824         * Fires when the element is checked or unchecked.
24825         * @param {Roo.bootstrap.CheckBox} this This input
24826         * @param {Boolean} checked The new checked value
24827         */
24828        check : true,
24829        /**
24830         * @event click
24831         * Fires when the element is click.
24832         * @param {Roo.bootstrap.CheckBox} this This input
24833         */
24834        click : true
24835     });
24836     
24837 };
24838
24839 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24840   
24841     inputType: 'checkbox',
24842     inputValue: 1,
24843     valueOff: 0,
24844     boxLabel: false,
24845     checked: false,
24846     weight : false,
24847     inline: false,
24848     tooltip : '',
24849     
24850     // checkbox success does not make any sense really.. 
24851     invalidClass : "",
24852     validClass : "",
24853     
24854     
24855     getAutoCreate : function()
24856     {
24857         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24858         
24859         var id = Roo.id();
24860         
24861         var cfg = {};
24862         
24863         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24864         
24865         if(this.inline){
24866             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24867         }
24868         
24869         var input =  {
24870             tag: 'input',
24871             id : id,
24872             type : this.inputType,
24873             value : this.inputValue,
24874             cls : 'roo-' + this.inputType, //'form-box',
24875             placeholder : this.placeholder || ''
24876             
24877         };
24878         
24879         if(this.inputType != 'radio'){
24880             var hidden =  {
24881                 tag: 'input',
24882                 type : 'hidden',
24883                 cls : 'roo-hidden-value',
24884                 value : this.checked ? this.inputValue : this.valueOff
24885             };
24886         }
24887         
24888             
24889         if (this.weight) { // Validity check?
24890             cfg.cls += " " + this.inputType + "-" + this.weight;
24891         }
24892         
24893         if (this.disabled) {
24894             input.disabled=true;
24895         }
24896         
24897         if(this.checked){
24898             input.checked = this.checked;
24899         }
24900         
24901         if (this.name) {
24902             
24903             input.name = this.name;
24904             
24905             if(this.inputType != 'radio'){
24906                 hidden.name = this.name;
24907                 input.name = '_hidden_' + this.name;
24908             }
24909         }
24910         
24911         if (this.size) {
24912             input.cls += ' input-' + this.size;
24913         }
24914         
24915         var settings=this;
24916         
24917         ['xs','sm','md','lg'].map(function(size){
24918             if (settings[size]) {
24919                 cfg.cls += ' col-' + size + '-' + settings[size];
24920             }
24921         });
24922         
24923         var inputblock = input;
24924          
24925         if (this.before || this.after) {
24926             
24927             inputblock = {
24928                 cls : 'input-group',
24929                 cn :  [] 
24930             };
24931             
24932             if (this.before) {
24933                 inputblock.cn.push({
24934                     tag :'span',
24935                     cls : 'input-group-addon',
24936                     html : this.before
24937                 });
24938             }
24939             
24940             inputblock.cn.push(input);
24941             
24942             if(this.inputType != 'radio'){
24943                 inputblock.cn.push(hidden);
24944             }
24945             
24946             if (this.after) {
24947                 inputblock.cn.push({
24948                     tag :'span',
24949                     cls : 'input-group-addon',
24950                     html : this.after
24951                 });
24952             }
24953             
24954         }
24955         var boxLabelCfg = false;
24956         
24957         if(this.boxLabel){
24958            
24959             boxLabelCfg = {
24960                 tag: 'label',
24961                 //'for': id, // box label is handled by onclick - so no for...
24962                 cls: 'box-label',
24963                 html: this.boxLabel
24964             };
24965             if(this.tooltip){
24966                 boxLabelCfg.tooltip = this.tooltip;
24967             }
24968              
24969         }
24970         
24971         
24972         if (align ==='left' && this.fieldLabel.length) {
24973 //                Roo.log("left and has label");
24974             cfg.cn = [
24975                 {
24976                     tag: 'label',
24977                     'for' :  id,
24978                     cls : 'control-label',
24979                     html : this.fieldLabel
24980                 },
24981                 {
24982                     cls : "", 
24983                     cn: [
24984                         inputblock
24985                     ]
24986                 }
24987             ];
24988             
24989             if (boxLabelCfg) {
24990                 cfg.cn[1].cn.push(boxLabelCfg);
24991             }
24992             
24993             if(this.labelWidth > 12){
24994                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24995             }
24996             
24997             if(this.labelWidth < 13 && this.labelmd == 0){
24998                 this.labelmd = this.labelWidth;
24999             }
25000             
25001             if(this.labellg > 0){
25002                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25003                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25004             }
25005             
25006             if(this.labelmd > 0){
25007                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25008                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25009             }
25010             
25011             if(this.labelsm > 0){
25012                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25013                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25014             }
25015             
25016             if(this.labelxs > 0){
25017                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25018                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25019             }
25020             
25021         } else if ( this.fieldLabel.length) {
25022 //                Roo.log(" label");
25023                 cfg.cn = [
25024                    
25025                     {
25026                         tag: this.boxLabel ? 'span' : 'label',
25027                         'for': id,
25028                         cls: 'control-label box-input-label',
25029                         //cls : 'input-group-addon',
25030                         html : this.fieldLabel
25031                     },
25032                     
25033                     inputblock
25034                     
25035                 ];
25036                 if (boxLabelCfg) {
25037                     cfg.cn.push(boxLabelCfg);
25038                 }
25039
25040         } else {
25041             
25042 //                Roo.log(" no label && no align");
25043                 cfg.cn = [  inputblock ] ;
25044                 if (boxLabelCfg) {
25045                     cfg.cn.push(boxLabelCfg);
25046                 }
25047
25048                 
25049         }
25050         
25051        
25052         
25053         if(this.inputType != 'radio'){
25054             cfg.cn.push(hidden);
25055         }
25056         
25057         return cfg;
25058         
25059     },
25060     
25061     /**
25062      * return the real input element.
25063      */
25064     inputEl: function ()
25065     {
25066         return this.el.select('input.roo-' + this.inputType,true).first();
25067     },
25068     hiddenEl: function ()
25069     {
25070         return this.el.select('input.roo-hidden-value',true).first();
25071     },
25072     
25073     labelEl: function()
25074     {
25075         return this.el.select('label.control-label',true).first();
25076     },
25077     /* depricated... */
25078     
25079     label: function()
25080     {
25081         return this.labelEl();
25082     },
25083     
25084     boxLabelEl: function()
25085     {
25086         return this.el.select('label.box-label',true).first();
25087     },
25088     
25089     initEvents : function()
25090     {
25091 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
25092         
25093         this.inputEl().on('click', this.onClick,  this);
25094         
25095         if (this.boxLabel) { 
25096             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25097         }
25098         
25099         this.startValue = this.getValue();
25100         
25101         if(this.groupId){
25102             Roo.bootstrap.CheckBox.register(this);
25103         }
25104     },
25105     
25106     onClick : function(e)
25107     {   
25108         if(this.fireEvent('click', this, e) !== false){
25109             this.setChecked(!this.checked);
25110         }
25111         
25112     },
25113     
25114     setChecked : function(state,suppressEvent)
25115     {
25116         this.startValue = this.getValue();
25117
25118         if(this.inputType == 'radio'){
25119             
25120             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25121                 e.dom.checked = false;
25122             });
25123             
25124             this.inputEl().dom.checked = true;
25125             
25126             this.inputEl().dom.value = this.inputValue;
25127             
25128             if(suppressEvent !== true){
25129                 this.fireEvent('check', this, true);
25130             }
25131             
25132             this.validate();
25133             
25134             return;
25135         }
25136         
25137         this.checked = state;
25138         
25139         this.inputEl().dom.checked = state;
25140         
25141         
25142         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25143         
25144         if(suppressEvent !== true){
25145             this.fireEvent('check', this, state);
25146         }
25147         
25148         this.validate();
25149     },
25150     
25151     getValue : function()
25152     {
25153         if(this.inputType == 'radio'){
25154             return this.getGroupValue();
25155         }
25156         
25157         return this.hiddenEl().dom.value;
25158         
25159     },
25160     
25161     getGroupValue : function()
25162     {
25163         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25164             return '';
25165         }
25166         
25167         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25168     },
25169     
25170     setValue : function(v,suppressEvent)
25171     {
25172         if(this.inputType == 'radio'){
25173             this.setGroupValue(v, suppressEvent);
25174             return;
25175         }
25176         
25177         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25178         
25179         this.validate();
25180     },
25181     
25182     setGroupValue : function(v, suppressEvent)
25183     {
25184         this.startValue = this.getValue();
25185         
25186         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25187             e.dom.checked = false;
25188             
25189             if(e.dom.value == v){
25190                 e.dom.checked = true;
25191             }
25192         });
25193         
25194         if(suppressEvent !== true){
25195             this.fireEvent('check', this, true);
25196         }
25197
25198         this.validate();
25199         
25200         return;
25201     },
25202     
25203     validate : function()
25204     {
25205         if(this.getVisibilityEl().hasClass('hidden')){
25206             return true;
25207         }
25208         
25209         if(
25210                 this.disabled || 
25211                 (this.inputType == 'radio' && this.validateRadio()) ||
25212                 (this.inputType == 'checkbox' && this.validateCheckbox())
25213         ){
25214             this.markValid();
25215             return true;
25216         }
25217         
25218         this.markInvalid();
25219         return false;
25220     },
25221     
25222     validateRadio : function()
25223     {
25224         if(this.getVisibilityEl().hasClass('hidden')){
25225             return true;
25226         }
25227         
25228         if(this.allowBlank){
25229             return true;
25230         }
25231         
25232         var valid = false;
25233         
25234         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25235             if(!e.dom.checked){
25236                 return;
25237             }
25238             
25239             valid = true;
25240             
25241             return false;
25242         });
25243         
25244         return valid;
25245     },
25246     
25247     validateCheckbox : function()
25248     {
25249         if(!this.groupId){
25250             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25251             //return (this.getValue() == this.inputValue) ? true : false;
25252         }
25253         
25254         var group = Roo.bootstrap.CheckBox.get(this.groupId);
25255         
25256         if(!group){
25257             return false;
25258         }
25259         
25260         var r = false;
25261         
25262         for(var i in group){
25263             if(group[i].el.isVisible(true)){
25264                 r = false;
25265                 break;
25266             }
25267             
25268             r = true;
25269         }
25270         
25271         for(var i in group){
25272             if(r){
25273                 break;
25274             }
25275             
25276             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25277         }
25278         
25279         return r;
25280     },
25281     
25282     /**
25283      * Mark this field as valid
25284      */
25285     markValid : function()
25286     {
25287         var _this = this;
25288         
25289         this.fireEvent('valid', this);
25290         
25291         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25292         
25293         if(this.groupId){
25294             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25295         }
25296         
25297         if(label){
25298             label.markValid();
25299         }
25300
25301         if(this.inputType == 'radio'){
25302             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25303                 var fg = e.findParent('.form-group', false, true);
25304                 if (Roo.bootstrap.version == 3) {
25305                     fg.removeClass([_this.invalidClass, _this.validClass]);
25306                     fg.addClass(_this.validClass);
25307                 } else {
25308                     fg.removeClass(['is-valid', 'is-invalid']);
25309                     fg.addClass('is-valid');
25310                 }
25311             });
25312             
25313             return;
25314         }
25315
25316         if(!this.groupId){
25317             var fg = this.el.findParent('.form-group', false, true);
25318             if (Roo.bootstrap.version == 3) {
25319                 fg.removeClass([this.invalidClass, this.validClass]);
25320                 fg.addClass(this.validClass);
25321             } else {
25322                 fg.removeClass(['is-valid', 'is-invalid']);
25323                 fg.addClass('is-valid');
25324             }
25325             return;
25326         }
25327         
25328         var group = Roo.bootstrap.CheckBox.get(this.groupId);
25329         
25330         if(!group){
25331             return;
25332         }
25333         
25334         for(var i in group){
25335             var fg = group[i].el.findParent('.form-group', false, true);
25336             if (Roo.bootstrap.version == 3) {
25337                 fg.removeClass([this.invalidClass, this.validClass]);
25338                 fg.addClass(this.validClass);
25339             } else {
25340                 fg.removeClass(['is-valid', 'is-invalid']);
25341                 fg.addClass('is-valid');
25342             }
25343         }
25344     },
25345     
25346      /**
25347      * Mark this field as invalid
25348      * @param {String} msg The validation message
25349      */
25350     markInvalid : function(msg)
25351     {
25352         if(this.allowBlank){
25353             return;
25354         }
25355         
25356         var _this = this;
25357         
25358         this.fireEvent('invalid', this, msg);
25359         
25360         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25361         
25362         if(this.groupId){
25363             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
25364         }
25365         
25366         if(label){
25367             label.markInvalid();
25368         }
25369             
25370         if(this.inputType == 'radio'){
25371             
25372             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25373                 var fg = e.findParent('.form-group', false, true);
25374                 if (Roo.bootstrap.version == 3) {
25375                     fg.removeClass([_this.invalidClass, _this.validClass]);
25376                     fg.addClass(_this.invalidClass);
25377                 } else {
25378                     fg.removeClass(['is-invalid', 'is-valid']);
25379                     fg.addClass('is-invalid');
25380                 }
25381             });
25382             
25383             return;
25384         }
25385         
25386         if(!this.groupId){
25387             var fg = this.el.findParent('.form-group', false, true);
25388             if (Roo.bootstrap.version == 3) {
25389                 fg.removeClass([_this.invalidClass, _this.validClass]);
25390                 fg.addClass(_this.invalidClass);
25391             } else {
25392                 fg.removeClass(['is-invalid', 'is-valid']);
25393                 fg.addClass('is-invalid');
25394             }
25395             return;
25396         }
25397         
25398         var group = Roo.bootstrap.CheckBox.get(this.groupId);
25399         
25400         if(!group){
25401             return;
25402         }
25403         
25404         for(var i in group){
25405             var fg = group[i].el.findParent('.form-group', false, true);
25406             if (Roo.bootstrap.version == 3) {
25407                 fg.removeClass([_this.invalidClass, _this.validClass]);
25408                 fg.addClass(_this.invalidClass);
25409             } else {
25410                 fg.removeClass(['is-invalid', 'is-valid']);
25411                 fg.addClass('is-invalid');
25412             }
25413         }
25414         
25415     },
25416     
25417     clearInvalid : function()
25418     {
25419         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25420         
25421         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25422         
25423         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25424         
25425         if (label && label.iconEl) {
25426             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25427             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25428         }
25429     },
25430     
25431     disable : function()
25432     {
25433         if(this.inputType != 'radio'){
25434             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25435             return;
25436         }
25437         
25438         var _this = this;
25439         
25440         if(this.rendered){
25441             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25442                 _this.getActionEl().addClass(this.disabledClass);
25443                 e.dom.disabled = true;
25444             });
25445         }
25446         
25447         this.disabled = true;
25448         this.fireEvent("disable", this);
25449         return this;
25450     },
25451
25452     enable : function()
25453     {
25454         if(this.inputType != 'radio'){
25455             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25456             return;
25457         }
25458         
25459         var _this = this;
25460         
25461         if(this.rendered){
25462             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25463                 _this.getActionEl().removeClass(this.disabledClass);
25464                 e.dom.disabled = false;
25465             });
25466         }
25467         
25468         this.disabled = false;
25469         this.fireEvent("enable", this);
25470         return this;
25471     },
25472     
25473     setBoxLabel : function(v)
25474     {
25475         this.boxLabel = v;
25476         
25477         if(this.rendered){
25478             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25479         }
25480     }
25481
25482 });
25483
25484 Roo.apply(Roo.bootstrap.CheckBox, {
25485     
25486     groups: {},
25487     
25488      /**
25489     * register a CheckBox Group
25490     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25491     */
25492     register : function(checkbox)
25493     {
25494         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25495             this.groups[checkbox.groupId] = {};
25496         }
25497         
25498         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25499             return;
25500         }
25501         
25502         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25503         
25504     },
25505     /**
25506     * fetch a CheckBox Group based on the group ID
25507     * @param {string} the group ID
25508     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25509     */
25510     get: function(groupId) {
25511         if (typeof(this.groups[groupId]) == 'undefined') {
25512             return false;
25513         }
25514         
25515         return this.groups[groupId] ;
25516     }
25517     
25518     
25519 });
25520 /*
25521  * - LGPL
25522  *
25523  * RadioItem
25524  * 
25525  */
25526
25527 /**
25528  * @class Roo.bootstrap.Radio
25529  * @extends Roo.bootstrap.Component
25530  * Bootstrap Radio class
25531  * @cfg {String} boxLabel - the label associated
25532  * @cfg {String} value - the value of radio
25533  * 
25534  * @constructor
25535  * Create a new Radio
25536  * @param {Object} config The config object
25537  */
25538 Roo.bootstrap.Radio = function(config){
25539     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25540     
25541 };
25542
25543 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25544     
25545     boxLabel : '',
25546     
25547     value : '',
25548     
25549     getAutoCreate : function()
25550     {
25551         var cfg = {
25552             tag : 'div',
25553             cls : 'form-group radio',
25554             cn : [
25555                 {
25556                     tag : 'label',
25557                     cls : 'box-label',
25558                     html : this.boxLabel
25559                 }
25560             ]
25561         };
25562         
25563         return cfg;
25564     },
25565     
25566     initEvents : function() 
25567     {
25568         this.parent().register(this);
25569         
25570         this.el.on('click', this.onClick, this);
25571         
25572     },
25573     
25574     onClick : function(e)
25575     {
25576         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25577             this.setChecked(true);
25578         }
25579     },
25580     
25581     setChecked : function(state, suppressEvent)
25582     {
25583         this.parent().setValue(this.value, suppressEvent);
25584         
25585     },
25586     
25587     setBoxLabel : function(v)
25588     {
25589         this.boxLabel = v;
25590         
25591         if(this.rendered){
25592             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25593         }
25594     }
25595     
25596 });
25597  
25598
25599  /*
25600  * - LGPL
25601  *
25602  * Input
25603  * 
25604  */
25605
25606 /**
25607  * @class Roo.bootstrap.SecurePass
25608  * @extends Roo.bootstrap.Input
25609  * Bootstrap SecurePass class
25610  *
25611  * 
25612  * @constructor
25613  * Create a new SecurePass
25614  * @param {Object} config The config object
25615  */
25616  
25617 Roo.bootstrap.SecurePass = function (config) {
25618     // these go here, so the translation tool can replace them..
25619     this.errors = {
25620         PwdEmpty: "Please type a password, and then retype it to confirm.",
25621         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25622         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25623         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25624         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25625         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25626         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25627         TooWeak: "Your password is Too Weak."
25628     },
25629     this.meterLabel = "Password strength:";
25630     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25631     this.meterClass = [
25632         "roo-password-meter-tooweak", 
25633         "roo-password-meter-weak", 
25634         "roo-password-meter-medium", 
25635         "roo-password-meter-strong", 
25636         "roo-password-meter-grey"
25637     ];
25638     
25639     this.errors = {};
25640     
25641     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25642 }
25643
25644 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25645     /**
25646      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25647      * {
25648      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25649      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25650      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25651      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25652      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25653      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25654      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25655      * })
25656      */
25657     // private
25658     
25659     meterWidth: 300,
25660     errorMsg :'',    
25661     errors: false,
25662     imageRoot: '/',
25663     /**
25664      * @cfg {String/Object} Label for the strength meter (defaults to
25665      * 'Password strength:')
25666      */
25667     // private
25668     meterLabel: '',
25669     /**
25670      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25671      * ['Weak', 'Medium', 'Strong'])
25672      */
25673     // private    
25674     pwdStrengths: false,    
25675     // private
25676     strength: 0,
25677     // private
25678     _lastPwd: null,
25679     // private
25680     kCapitalLetter: 0,
25681     kSmallLetter: 1,
25682     kDigit: 2,
25683     kPunctuation: 3,
25684     
25685     insecure: false,
25686     // private
25687     initEvents: function ()
25688     {
25689         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25690
25691         if (this.el.is('input[type=password]') && Roo.isSafari) {
25692             this.el.on('keydown', this.SafariOnKeyDown, this);
25693         }
25694
25695         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25696     },
25697     // private
25698     onRender: function (ct, position)
25699     {
25700         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25701         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25702         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25703
25704         this.trigger.createChild({
25705                    cn: [
25706                     {
25707                     //id: 'PwdMeter',
25708                     tag: 'div',
25709                     cls: 'roo-password-meter-grey col-xs-12',
25710                     style: {
25711                         //width: 0,
25712                         //width: this.meterWidth + 'px'                                                
25713                         }
25714                     },
25715                     {                            
25716                          cls: 'roo-password-meter-text'                          
25717                     }
25718                 ]            
25719         });
25720
25721          
25722         if (this.hideTrigger) {
25723             this.trigger.setDisplayed(false);
25724         }
25725         this.setSize(this.width || '', this.height || '');
25726     },
25727     // private
25728     onDestroy: function ()
25729     {
25730         if (this.trigger) {
25731             this.trigger.removeAllListeners();
25732             this.trigger.remove();
25733         }
25734         if (this.wrap) {
25735             this.wrap.remove();
25736         }
25737         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25738     },
25739     // private
25740     checkStrength: function ()
25741     {
25742         var pwd = this.inputEl().getValue();
25743         if (pwd == this._lastPwd) {
25744             return;
25745         }
25746
25747         var strength;
25748         if (this.ClientSideStrongPassword(pwd)) {
25749             strength = 3;
25750         } else if (this.ClientSideMediumPassword(pwd)) {
25751             strength = 2;
25752         } else if (this.ClientSideWeakPassword(pwd)) {
25753             strength = 1;
25754         } else {
25755             strength = 0;
25756         }
25757         
25758         Roo.log('strength1: ' + strength);
25759         
25760         //var pm = this.trigger.child('div/div/div').dom;
25761         var pm = this.trigger.child('div/div');
25762         pm.removeClass(this.meterClass);
25763         pm.addClass(this.meterClass[strength]);
25764                 
25765         
25766         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25767                 
25768         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25769         
25770         this._lastPwd = pwd;
25771     },
25772     reset: function ()
25773     {
25774         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25775         
25776         this._lastPwd = '';
25777         
25778         var pm = this.trigger.child('div/div');
25779         pm.removeClass(this.meterClass);
25780         pm.addClass('roo-password-meter-grey');        
25781         
25782         
25783         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25784         
25785         pt.innerHTML = '';
25786         this.inputEl().dom.type='password';
25787     },
25788     // private
25789     validateValue: function (value)
25790     {
25791         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25792             return false;
25793         }
25794         if (value.length == 0) {
25795             if (this.allowBlank) {
25796                 this.clearInvalid();
25797                 return true;
25798             }
25799
25800             this.markInvalid(this.errors.PwdEmpty);
25801             this.errorMsg = this.errors.PwdEmpty;
25802             return false;
25803         }
25804         
25805         if(this.insecure){
25806             return true;
25807         }
25808         
25809         if (!value.match(/[\x21-\x7e]+/)) {
25810             this.markInvalid(this.errors.PwdBadChar);
25811             this.errorMsg = this.errors.PwdBadChar;
25812             return false;
25813         }
25814         if (value.length < 6) {
25815             this.markInvalid(this.errors.PwdShort);
25816             this.errorMsg = this.errors.PwdShort;
25817             return false;
25818         }
25819         if (value.length > 16) {
25820             this.markInvalid(this.errors.PwdLong);
25821             this.errorMsg = this.errors.PwdLong;
25822             return false;
25823         }
25824         var strength;
25825         if (this.ClientSideStrongPassword(value)) {
25826             strength = 3;
25827         } else if (this.ClientSideMediumPassword(value)) {
25828             strength = 2;
25829         } else if (this.ClientSideWeakPassword(value)) {
25830             strength = 1;
25831         } else {
25832             strength = 0;
25833         }
25834
25835         
25836         if (strength < 2) {
25837             //this.markInvalid(this.errors.TooWeak);
25838             this.errorMsg = this.errors.TooWeak;
25839             //return false;
25840         }
25841         
25842         
25843         console.log('strength2: ' + strength);
25844         
25845         //var pm = this.trigger.child('div/div/div').dom;
25846         
25847         var pm = this.trigger.child('div/div');
25848         pm.removeClass(this.meterClass);
25849         pm.addClass(this.meterClass[strength]);
25850                 
25851         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25852                 
25853         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25854         
25855         this.errorMsg = ''; 
25856         return true;
25857     },
25858     // private
25859     CharacterSetChecks: function (type)
25860     {
25861         this.type = type;
25862         this.fResult = false;
25863     },
25864     // private
25865     isctype: function (character, type)
25866     {
25867         switch (type) {  
25868             case this.kCapitalLetter:
25869                 if (character >= 'A' && character <= 'Z') {
25870                     return true;
25871                 }
25872                 break;
25873             
25874             case this.kSmallLetter:
25875                 if (character >= 'a' && character <= 'z') {
25876                     return true;
25877                 }
25878                 break;
25879             
25880             case this.kDigit:
25881                 if (character >= '0' && character <= '9') {
25882                     return true;
25883                 }
25884                 break;
25885             
25886             case this.kPunctuation:
25887                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25888                     return true;
25889                 }
25890                 break;
25891             
25892             default:
25893                 return false;
25894         }
25895
25896     },
25897     // private
25898     IsLongEnough: function (pwd, size)
25899     {
25900         return !(pwd == null || isNaN(size) || pwd.length < size);
25901     },
25902     // private
25903     SpansEnoughCharacterSets: function (word, nb)
25904     {
25905         if (!this.IsLongEnough(word, nb))
25906         {
25907             return false;
25908         }
25909
25910         var characterSetChecks = new Array(
25911             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25912             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25913         );
25914         
25915         for (var index = 0; index < word.length; ++index) {
25916             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25917                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25918                     characterSetChecks[nCharSet].fResult = true;
25919                     break;
25920                 }
25921             }
25922         }
25923
25924         var nCharSets = 0;
25925         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25926             if (characterSetChecks[nCharSet].fResult) {
25927                 ++nCharSets;
25928             }
25929         }
25930
25931         if (nCharSets < nb) {
25932             return false;
25933         }
25934         return true;
25935     },
25936     // private
25937     ClientSideStrongPassword: function (pwd)
25938     {
25939         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25940     },
25941     // private
25942     ClientSideMediumPassword: function (pwd)
25943     {
25944         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25945     },
25946     // private
25947     ClientSideWeakPassword: function (pwd)
25948     {
25949         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25950     }
25951           
25952 })//<script type="text/javascript">
25953
25954 /*
25955  * Based  Ext JS Library 1.1.1
25956  * Copyright(c) 2006-2007, Ext JS, LLC.
25957  * LGPL
25958  *
25959  */
25960  
25961 /**
25962  * @class Roo.HtmlEditorCore
25963  * @extends Roo.Component
25964  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25965  *
25966  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25967  */
25968
25969 Roo.HtmlEditorCore = function(config){
25970     
25971     
25972     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25973     
25974     
25975     this.addEvents({
25976         /**
25977          * @event initialize
25978          * Fires when the editor is fully initialized (including the iframe)
25979          * @param {Roo.HtmlEditorCore} this
25980          */
25981         initialize: true,
25982         /**
25983          * @event activate
25984          * Fires when the editor is first receives the focus. Any insertion must wait
25985          * until after this event.
25986          * @param {Roo.HtmlEditorCore} this
25987          */
25988         activate: true,
25989          /**
25990          * @event beforesync
25991          * Fires before the textarea is updated with content from the editor iframe. Return false
25992          * to cancel the sync.
25993          * @param {Roo.HtmlEditorCore} this
25994          * @param {String} html
25995          */
25996         beforesync: true,
25997          /**
25998          * @event beforepush
25999          * Fires before the iframe editor is updated with content from the textarea. Return false
26000          * to cancel the push.
26001          * @param {Roo.HtmlEditorCore} this
26002          * @param {String} html
26003          */
26004         beforepush: true,
26005          /**
26006          * @event sync
26007          * Fires when the textarea is updated with content from the editor iframe.
26008          * @param {Roo.HtmlEditorCore} this
26009          * @param {String} html
26010          */
26011         sync: true,
26012          /**
26013          * @event push
26014          * Fires when the iframe editor is updated with content from the textarea.
26015          * @param {Roo.HtmlEditorCore} this
26016          * @param {String} html
26017          */
26018         push: true,
26019         
26020         /**
26021          * @event editorevent
26022          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26023          * @param {Roo.HtmlEditorCore} this
26024          */
26025         editorevent: true
26026         
26027     });
26028     
26029     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26030     
26031     // defaults : white / black...
26032     this.applyBlacklists();
26033     
26034     
26035     
26036 };
26037
26038
26039 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26040
26041
26042      /**
26043      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26044      */
26045     
26046     owner : false,
26047     
26048      /**
26049      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26050      *                        Roo.resizable.
26051      */
26052     resizable : false,
26053      /**
26054      * @cfg {Number} height (in pixels)
26055      */   
26056     height: 300,
26057    /**
26058      * @cfg {Number} width (in pixels)
26059      */   
26060     width: 500,
26061     
26062     /**
26063      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26064      * 
26065      */
26066     stylesheets: false,
26067     
26068     /**
26069      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26070      */
26071     allowComments: false,
26072     // id of frame..
26073     frameId: false,
26074     
26075     // private properties
26076     validationEvent : false,
26077     deferHeight: true,
26078     initialized : false,
26079     activated : false,
26080     sourceEditMode : false,
26081     onFocus : Roo.emptyFn,
26082     iframePad:3,
26083     hideMode:'offsets',
26084     
26085     clearUp: true,
26086     
26087     // blacklist + whitelisted elements..
26088     black: false,
26089     white: false,
26090      
26091     bodyCls : '',
26092
26093     /**
26094      * Protected method that will not generally be called directly. It
26095      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26096      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26097      */
26098     getDocMarkup : function(){
26099         // body styles..
26100         var st = '';
26101         
26102         // inherit styels from page...?? 
26103         if (this.stylesheets === false) {
26104             
26105             Roo.get(document.head).select('style').each(function(node) {
26106                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26107             });
26108             
26109             Roo.get(document.head).select('link').each(function(node) { 
26110                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26111             });
26112             
26113         } else if (!this.stylesheets.length) {
26114                 // simple..
26115                 st = '<style type="text/css">' +
26116                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26117                    '</style>';
26118         } else {
26119             for (var i in this.stylesheets) { 
26120                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26121             }
26122             
26123         }
26124         
26125         st +=  '<style type="text/css">' +
26126             'IMG { cursor: pointer } ' +
26127         '</style>';
26128
26129         var cls = 'roo-htmleditor-body';
26130         
26131         if(this.bodyCls.length){
26132             cls += ' ' + this.bodyCls;
26133         }
26134         
26135         return '<html><head>' + st  +
26136             //<style type="text/css">' +
26137             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26138             //'</style>' +
26139             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26140     },
26141
26142     // private
26143     onRender : function(ct, position)
26144     {
26145         var _t = this;
26146         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26147         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26148         
26149         
26150         this.el.dom.style.border = '0 none';
26151         this.el.dom.setAttribute('tabIndex', -1);
26152         this.el.addClass('x-hidden hide');
26153         
26154         
26155         
26156         if(Roo.isIE){ // fix IE 1px bogus margin
26157             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26158         }
26159        
26160         
26161         this.frameId = Roo.id();
26162         
26163          
26164         
26165         var iframe = this.owner.wrap.createChild({
26166             tag: 'iframe',
26167             cls: 'form-control', // bootstrap..
26168             id: this.frameId,
26169             name: this.frameId,
26170             frameBorder : 'no',
26171             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26172         }, this.el
26173         );
26174         
26175         
26176         this.iframe = iframe.dom;
26177
26178          this.assignDocWin();
26179         
26180         this.doc.designMode = 'on';
26181        
26182         this.doc.open();
26183         this.doc.write(this.getDocMarkup());
26184         this.doc.close();
26185
26186         
26187         var task = { // must defer to wait for browser to be ready
26188             run : function(){
26189                 //console.log("run task?" + this.doc.readyState);
26190                 this.assignDocWin();
26191                 if(this.doc.body || this.doc.readyState == 'complete'){
26192                     try {
26193                         this.doc.designMode="on";
26194                     } catch (e) {
26195                         return;
26196                     }
26197                     Roo.TaskMgr.stop(task);
26198                     this.initEditor.defer(10, this);
26199                 }
26200             },
26201             interval : 10,
26202             duration: 10000,
26203             scope: this
26204         };
26205         Roo.TaskMgr.start(task);
26206
26207     },
26208
26209     // private
26210     onResize : function(w, h)
26211     {
26212          Roo.log('resize: ' +w + ',' + h );
26213         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26214         if(!this.iframe){
26215             return;
26216         }
26217         if(typeof w == 'number'){
26218             
26219             this.iframe.style.width = w + 'px';
26220         }
26221         if(typeof h == 'number'){
26222             
26223             this.iframe.style.height = h + 'px';
26224             if(this.doc){
26225                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26226             }
26227         }
26228         
26229     },
26230
26231     /**
26232      * Toggles the editor between standard and source edit mode.
26233      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26234      */
26235     toggleSourceEdit : function(sourceEditMode){
26236         
26237         this.sourceEditMode = sourceEditMode === true;
26238         
26239         if(this.sourceEditMode){
26240  
26241             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26242             
26243         }else{
26244             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26245             //this.iframe.className = '';
26246             this.deferFocus();
26247         }
26248         //this.setSize(this.owner.wrap.getSize());
26249         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26250     },
26251
26252     
26253   
26254
26255     /**
26256      * Protected method that will not generally be called directly. If you need/want
26257      * custom HTML cleanup, this is the method you should override.
26258      * @param {String} html The HTML to be cleaned
26259      * return {String} The cleaned HTML
26260      */
26261     cleanHtml : function(html){
26262         html = String(html);
26263         if(html.length > 5){
26264             if(Roo.isSafari){ // strip safari nonsense
26265                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26266             }
26267         }
26268         if(html == '&nbsp;'){
26269             html = '';
26270         }
26271         return html;
26272     },
26273
26274     /**
26275      * HTML Editor -> Textarea
26276      * Protected method that will not generally be called directly. Syncs the contents
26277      * of the editor iframe with the textarea.
26278      */
26279     syncValue : function(){
26280         if(this.initialized){
26281             var bd = (this.doc.body || this.doc.documentElement);
26282             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26283             var html = bd.innerHTML;
26284             if(Roo.isSafari){
26285                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26286                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26287                 if(m && m[1]){
26288                     html = '<div style="'+m[0]+'">' + html + '</div>';
26289                 }
26290             }
26291             html = this.cleanHtml(html);
26292             // fix up the special chars.. normaly like back quotes in word...
26293             // however we do not want to do this with chinese..
26294             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26295                 
26296                 var cc = match.charCodeAt();
26297
26298                 // Get the character value, handling surrogate pairs
26299                 if (match.length == 2) {
26300                     // It's a surrogate pair, calculate the Unicode code point
26301                     var high = match.charCodeAt(0) - 0xD800;
26302                     var low  = match.charCodeAt(1) - 0xDC00;
26303                     cc = (high * 0x400) + low + 0x10000;
26304                 }  else if (
26305                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26306                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26307                     (cc >= 0xf900 && cc < 0xfb00 )
26308                 ) {
26309                         return match;
26310                 }  
26311          
26312                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26313                 return "&#" + cc + ";";
26314                 
26315                 
26316             });
26317             
26318             
26319              
26320             if(this.owner.fireEvent('beforesync', this, html) !== false){
26321                 this.el.dom.value = html;
26322                 this.owner.fireEvent('sync', this, html);
26323             }
26324         }
26325     },
26326
26327     /**
26328      * Protected method that will not generally be called directly. Pushes the value of the textarea
26329      * into the iframe editor.
26330      */
26331     pushValue : function(){
26332         if(this.initialized){
26333             var v = this.el.dom.value.trim();
26334             
26335 //            if(v.length < 1){
26336 //                v = '&#160;';
26337 //            }
26338             
26339             if(this.owner.fireEvent('beforepush', this, v) !== false){
26340                 var d = (this.doc.body || this.doc.documentElement);
26341                 d.innerHTML = v;
26342                 this.cleanUpPaste();
26343                 this.el.dom.value = d.innerHTML;
26344                 this.owner.fireEvent('push', this, v);
26345             }
26346         }
26347     },
26348
26349     // private
26350     deferFocus : function(){
26351         this.focus.defer(10, this);
26352     },
26353
26354     // doc'ed in Field
26355     focus : function(){
26356         if(this.win && !this.sourceEditMode){
26357             this.win.focus();
26358         }else{
26359             this.el.focus();
26360         }
26361     },
26362     
26363     assignDocWin: function()
26364     {
26365         var iframe = this.iframe;
26366         
26367          if(Roo.isIE){
26368             this.doc = iframe.contentWindow.document;
26369             this.win = iframe.contentWindow;
26370         } else {
26371 //            if (!Roo.get(this.frameId)) {
26372 //                return;
26373 //            }
26374 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26375 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26376             
26377             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26378                 return;
26379             }
26380             
26381             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26382             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26383         }
26384     },
26385     
26386     // private
26387     initEditor : function(){
26388         //console.log("INIT EDITOR");
26389         this.assignDocWin();
26390         
26391         
26392         
26393         this.doc.designMode="on";
26394         this.doc.open();
26395         this.doc.write(this.getDocMarkup());
26396         this.doc.close();
26397         
26398         var dbody = (this.doc.body || this.doc.documentElement);
26399         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26400         // this copies styles from the containing element into thsi one..
26401         // not sure why we need all of this..
26402         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26403         
26404         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26405         //ss['background-attachment'] = 'fixed'; // w3c
26406         dbody.bgProperties = 'fixed'; // ie
26407         //Roo.DomHelper.applyStyles(dbody, ss);
26408         Roo.EventManager.on(this.doc, {
26409             //'mousedown': this.onEditorEvent,
26410             'mouseup': this.onEditorEvent,
26411             'dblclick': this.onEditorEvent,
26412             'click': this.onEditorEvent,
26413             'keyup': this.onEditorEvent,
26414             buffer:100,
26415             scope: this
26416         });
26417         if(Roo.isGecko){
26418             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26419         }
26420         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26421             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26422         }
26423         this.initialized = true;
26424
26425         this.owner.fireEvent('initialize', this);
26426         this.pushValue();
26427     },
26428
26429     // private
26430     onDestroy : function(){
26431         
26432         
26433         
26434         if(this.rendered){
26435             
26436             //for (var i =0; i < this.toolbars.length;i++) {
26437             //    // fixme - ask toolbars for heights?
26438             //    this.toolbars[i].onDestroy();
26439            // }
26440             
26441             //this.wrap.dom.innerHTML = '';
26442             //this.wrap.remove();
26443         }
26444     },
26445
26446     // private
26447     onFirstFocus : function(){
26448         
26449         this.assignDocWin();
26450         
26451         
26452         this.activated = true;
26453          
26454     
26455         if(Roo.isGecko){ // prevent silly gecko errors
26456             this.win.focus();
26457             var s = this.win.getSelection();
26458             if(!s.focusNode || s.focusNode.nodeType != 3){
26459                 var r = s.getRangeAt(0);
26460                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26461                 r.collapse(true);
26462                 this.deferFocus();
26463             }
26464             try{
26465                 this.execCmd('useCSS', true);
26466                 this.execCmd('styleWithCSS', false);
26467             }catch(e){}
26468         }
26469         this.owner.fireEvent('activate', this);
26470     },
26471
26472     // private
26473     adjustFont: function(btn){
26474         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26475         //if(Roo.isSafari){ // safari
26476         //    adjust *= 2;
26477        // }
26478         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26479         if(Roo.isSafari){ // safari
26480             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26481             v =  (v < 10) ? 10 : v;
26482             v =  (v > 48) ? 48 : v;
26483             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26484             
26485         }
26486         
26487         
26488         v = Math.max(1, v+adjust);
26489         
26490         this.execCmd('FontSize', v  );
26491     },
26492
26493     onEditorEvent : function(e)
26494     {
26495         this.owner.fireEvent('editorevent', this, e);
26496       //  this.updateToolbar();
26497         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26498     },
26499
26500     insertTag : function(tg)
26501     {
26502         // could be a bit smarter... -> wrap the current selected tRoo..
26503         if (tg.toLowerCase() == 'span' ||
26504             tg.toLowerCase() == 'code' ||
26505             tg.toLowerCase() == 'sup' ||
26506             tg.toLowerCase() == 'sub' 
26507             ) {
26508             
26509             range = this.createRange(this.getSelection());
26510             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26511             wrappingNode.appendChild(range.extractContents());
26512             range.insertNode(wrappingNode);
26513
26514             return;
26515             
26516             
26517             
26518         }
26519         this.execCmd("formatblock",   tg);
26520         
26521     },
26522     
26523     insertText : function(txt)
26524     {
26525         
26526         
26527         var range = this.createRange();
26528         range.deleteContents();
26529                //alert(Sender.getAttribute('label'));
26530                
26531         range.insertNode(this.doc.createTextNode(txt));
26532     } ,
26533     
26534      
26535
26536     /**
26537      * Executes a Midas editor command on the editor document and performs necessary focus and
26538      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26539      * @param {String} cmd The Midas command
26540      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26541      */
26542     relayCmd : function(cmd, value){
26543         this.win.focus();
26544         this.execCmd(cmd, value);
26545         this.owner.fireEvent('editorevent', this);
26546         //this.updateToolbar();
26547         this.owner.deferFocus();
26548     },
26549
26550     /**
26551      * Executes a Midas editor command directly on the editor document.
26552      * For visual commands, you should use {@link #relayCmd} instead.
26553      * <b>This should only be called after the editor is initialized.</b>
26554      * @param {String} cmd The Midas command
26555      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26556      */
26557     execCmd : function(cmd, value){
26558         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26559         this.syncValue();
26560     },
26561  
26562  
26563    
26564     /**
26565      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26566      * to insert tRoo.
26567      * @param {String} text | dom node.. 
26568      */
26569     insertAtCursor : function(text)
26570     {
26571         
26572         if(!this.activated){
26573             return;
26574         }
26575         /*
26576         if(Roo.isIE){
26577             this.win.focus();
26578             var r = this.doc.selection.createRange();
26579             if(r){
26580                 r.collapse(true);
26581                 r.pasteHTML(text);
26582                 this.syncValue();
26583                 this.deferFocus();
26584             
26585             }
26586             return;
26587         }
26588         */
26589         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26590             this.win.focus();
26591             
26592             
26593             // from jquery ui (MIT licenced)
26594             var range, node;
26595             var win = this.win;
26596             
26597             if (win.getSelection && win.getSelection().getRangeAt) {
26598                 range = win.getSelection().getRangeAt(0);
26599                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26600                 range.insertNode(node);
26601             } else if (win.document.selection && win.document.selection.createRange) {
26602                 // no firefox support
26603                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26604                 win.document.selection.createRange().pasteHTML(txt);
26605             } else {
26606                 // no firefox support
26607                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26608                 this.execCmd('InsertHTML', txt);
26609             } 
26610             
26611             this.syncValue();
26612             
26613             this.deferFocus();
26614         }
26615     },
26616  // private
26617     mozKeyPress : function(e){
26618         if(e.ctrlKey){
26619             var c = e.getCharCode(), cmd;
26620           
26621             if(c > 0){
26622                 c = String.fromCharCode(c).toLowerCase();
26623                 switch(c){
26624                     case 'b':
26625                         cmd = 'bold';
26626                         break;
26627                     case 'i':
26628                         cmd = 'italic';
26629                         break;
26630                     
26631                     case 'u':
26632                         cmd = 'underline';
26633                         break;
26634                     
26635                     case 'v':
26636                         this.cleanUpPaste.defer(100, this);
26637                         return;
26638                         
26639                 }
26640                 if(cmd){
26641                     this.win.focus();
26642                     this.execCmd(cmd);
26643                     this.deferFocus();
26644                     e.preventDefault();
26645                 }
26646                 
26647             }
26648         }
26649     },
26650
26651     // private
26652     fixKeys : function(){ // load time branching for fastest keydown performance
26653         if(Roo.isIE){
26654             return function(e){
26655                 var k = e.getKey(), r;
26656                 if(k == e.TAB){
26657                     e.stopEvent();
26658                     r = this.doc.selection.createRange();
26659                     if(r){
26660                         r.collapse(true);
26661                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26662                         this.deferFocus();
26663                     }
26664                     return;
26665                 }
26666                 
26667                 if(k == e.ENTER){
26668                     r = this.doc.selection.createRange();
26669                     if(r){
26670                         var target = r.parentElement();
26671                         if(!target || target.tagName.toLowerCase() != 'li'){
26672                             e.stopEvent();
26673                             r.pasteHTML('<br />');
26674                             r.collapse(false);
26675                             r.select();
26676                         }
26677                     }
26678                 }
26679                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26680                     this.cleanUpPaste.defer(100, this);
26681                     return;
26682                 }
26683                 
26684                 
26685             };
26686         }else if(Roo.isOpera){
26687             return function(e){
26688                 var k = e.getKey();
26689                 if(k == e.TAB){
26690                     e.stopEvent();
26691                     this.win.focus();
26692                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26693                     this.deferFocus();
26694                 }
26695                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26696                     this.cleanUpPaste.defer(100, this);
26697                     return;
26698                 }
26699                 
26700             };
26701         }else if(Roo.isSafari){
26702             return function(e){
26703                 var k = e.getKey();
26704                 
26705                 if(k == e.TAB){
26706                     e.stopEvent();
26707                     this.execCmd('InsertText','\t');
26708                     this.deferFocus();
26709                     return;
26710                 }
26711                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26712                     this.cleanUpPaste.defer(100, this);
26713                     return;
26714                 }
26715                 
26716              };
26717         }
26718     }(),
26719     
26720     getAllAncestors: function()
26721     {
26722         var p = this.getSelectedNode();
26723         var a = [];
26724         if (!p) {
26725             a.push(p); // push blank onto stack..
26726             p = this.getParentElement();
26727         }
26728         
26729         
26730         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26731             a.push(p);
26732             p = p.parentNode;
26733         }
26734         a.push(this.doc.body);
26735         return a;
26736     },
26737     lastSel : false,
26738     lastSelNode : false,
26739     
26740     
26741     getSelection : function() 
26742     {
26743         this.assignDocWin();
26744         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26745     },
26746     
26747     getSelectedNode: function() 
26748     {
26749         // this may only work on Gecko!!!
26750         
26751         // should we cache this!!!!
26752         
26753         
26754         
26755          
26756         var range = this.createRange(this.getSelection()).cloneRange();
26757         
26758         if (Roo.isIE) {
26759             var parent = range.parentElement();
26760             while (true) {
26761                 var testRange = range.duplicate();
26762                 testRange.moveToElementText(parent);
26763                 if (testRange.inRange(range)) {
26764                     break;
26765                 }
26766                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26767                     break;
26768                 }
26769                 parent = parent.parentElement;
26770             }
26771             return parent;
26772         }
26773         
26774         // is ancestor a text element.
26775         var ac =  range.commonAncestorContainer;
26776         if (ac.nodeType == 3) {
26777             ac = ac.parentNode;
26778         }
26779         
26780         var ar = ac.childNodes;
26781          
26782         var nodes = [];
26783         var other_nodes = [];
26784         var has_other_nodes = false;
26785         for (var i=0;i<ar.length;i++) {
26786             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26787                 continue;
26788             }
26789             // fullly contained node.
26790             
26791             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26792                 nodes.push(ar[i]);
26793                 continue;
26794             }
26795             
26796             // probably selected..
26797             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26798                 other_nodes.push(ar[i]);
26799                 continue;
26800             }
26801             // outer..
26802             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26803                 continue;
26804             }
26805             
26806             
26807             has_other_nodes = true;
26808         }
26809         if (!nodes.length && other_nodes.length) {
26810             nodes= other_nodes;
26811         }
26812         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26813             return false;
26814         }
26815         
26816         return nodes[0];
26817     },
26818     createRange: function(sel)
26819     {
26820         // this has strange effects when using with 
26821         // top toolbar - not sure if it's a great idea.
26822         //this.editor.contentWindow.focus();
26823         if (typeof sel != "undefined") {
26824             try {
26825                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26826             } catch(e) {
26827                 return this.doc.createRange();
26828             }
26829         } else {
26830             return this.doc.createRange();
26831         }
26832     },
26833     getParentElement: function()
26834     {
26835         
26836         this.assignDocWin();
26837         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26838         
26839         var range = this.createRange(sel);
26840          
26841         try {
26842             var p = range.commonAncestorContainer;
26843             while (p.nodeType == 3) { // text node
26844                 p = p.parentNode;
26845             }
26846             return p;
26847         } catch (e) {
26848             return null;
26849         }
26850     
26851     },
26852     /***
26853      *
26854      * Range intersection.. the hard stuff...
26855      *  '-1' = before
26856      *  '0' = hits..
26857      *  '1' = after.
26858      *         [ -- selected range --- ]
26859      *   [fail]                        [fail]
26860      *
26861      *    basically..
26862      *      if end is before start or  hits it. fail.
26863      *      if start is after end or hits it fail.
26864      *
26865      *   if either hits (but other is outside. - then it's not 
26866      *   
26867      *    
26868      **/
26869     
26870     
26871     // @see http://www.thismuchiknow.co.uk/?p=64.
26872     rangeIntersectsNode : function(range, node)
26873     {
26874         var nodeRange = node.ownerDocument.createRange();
26875         try {
26876             nodeRange.selectNode(node);
26877         } catch (e) {
26878             nodeRange.selectNodeContents(node);
26879         }
26880     
26881         var rangeStartRange = range.cloneRange();
26882         rangeStartRange.collapse(true);
26883     
26884         var rangeEndRange = range.cloneRange();
26885         rangeEndRange.collapse(false);
26886     
26887         var nodeStartRange = nodeRange.cloneRange();
26888         nodeStartRange.collapse(true);
26889     
26890         var nodeEndRange = nodeRange.cloneRange();
26891         nodeEndRange.collapse(false);
26892     
26893         return rangeStartRange.compareBoundaryPoints(
26894                  Range.START_TO_START, nodeEndRange) == -1 &&
26895                rangeEndRange.compareBoundaryPoints(
26896                  Range.START_TO_START, nodeStartRange) == 1;
26897         
26898          
26899     },
26900     rangeCompareNode : function(range, node)
26901     {
26902         var nodeRange = node.ownerDocument.createRange();
26903         try {
26904             nodeRange.selectNode(node);
26905         } catch (e) {
26906             nodeRange.selectNodeContents(node);
26907         }
26908         
26909         
26910         range.collapse(true);
26911     
26912         nodeRange.collapse(true);
26913      
26914         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26915         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26916          
26917         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26918         
26919         var nodeIsBefore   =  ss == 1;
26920         var nodeIsAfter    = ee == -1;
26921         
26922         if (nodeIsBefore && nodeIsAfter) {
26923             return 0; // outer
26924         }
26925         if (!nodeIsBefore && nodeIsAfter) {
26926             return 1; //right trailed.
26927         }
26928         
26929         if (nodeIsBefore && !nodeIsAfter) {
26930             return 2;  // left trailed.
26931         }
26932         // fully contined.
26933         return 3;
26934     },
26935
26936     // private? - in a new class?
26937     cleanUpPaste :  function()
26938     {
26939         // cleans up the whole document..
26940         Roo.log('cleanuppaste');
26941         
26942         this.cleanUpChildren(this.doc.body);
26943         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26944         if (clean != this.doc.body.innerHTML) {
26945             this.doc.body.innerHTML = clean;
26946         }
26947         
26948     },
26949     
26950     cleanWordChars : function(input) {// change the chars to hex code
26951         var he = Roo.HtmlEditorCore;
26952         
26953         var output = input;
26954         Roo.each(he.swapCodes, function(sw) { 
26955             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26956             
26957             output = output.replace(swapper, sw[1]);
26958         });
26959         
26960         return output;
26961     },
26962     
26963     
26964     cleanUpChildren : function (n)
26965     {
26966         if (!n.childNodes.length) {
26967             return;
26968         }
26969         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26970            this.cleanUpChild(n.childNodes[i]);
26971         }
26972     },
26973     
26974     
26975         
26976     
26977     cleanUpChild : function (node)
26978     {
26979         var ed = this;
26980         //console.log(node);
26981         if (node.nodeName == "#text") {
26982             // clean up silly Windows -- stuff?
26983             return; 
26984         }
26985         if (node.nodeName == "#comment") {
26986             if (!this.allowComments) {
26987                 node.parentNode.removeChild(node);
26988             }
26989             // clean up silly Windows -- stuff?
26990             return; 
26991         }
26992         var lcname = node.tagName.toLowerCase();
26993         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26994         // whitelist of tags..
26995         
26996         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26997             // remove node.
26998             node.parentNode.removeChild(node);
26999             return;
27000             
27001         }
27002         
27003         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27004         
27005         // spans with no attributes - just remove them..
27006         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27007             remove_keep_children = true;
27008         }
27009         
27010         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27011         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27012         
27013         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27014         //    remove_keep_children = true;
27015         //}
27016         
27017         if (remove_keep_children) {
27018             this.cleanUpChildren(node);
27019             // inserts everything just before this node...
27020             while (node.childNodes.length) {
27021                 var cn = node.childNodes[0];
27022                 node.removeChild(cn);
27023                 node.parentNode.insertBefore(cn, node);
27024             }
27025             node.parentNode.removeChild(node);
27026             return;
27027         }
27028         
27029         if (!node.attributes || !node.attributes.length) {
27030             
27031           
27032             
27033             
27034             this.cleanUpChildren(node);
27035             return;
27036         }
27037         
27038         function cleanAttr(n,v)
27039         {
27040             
27041             if (v.match(/^\./) || v.match(/^\//)) {
27042                 return;
27043             }
27044             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27045                 return;
27046             }
27047             if (v.match(/^#/)) {
27048                 return;
27049             }
27050             if (v.match(/^\{/)) { // allow template editing.
27051                 return;
27052             }
27053 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27054             node.removeAttribute(n);
27055             
27056         }
27057         
27058         var cwhite = this.cwhite;
27059         var cblack = this.cblack;
27060             
27061         function cleanStyle(n,v)
27062         {
27063             if (v.match(/expression/)) { //XSS?? should we even bother..
27064                 node.removeAttribute(n);
27065                 return;
27066             }
27067             
27068             var parts = v.split(/;/);
27069             var clean = [];
27070             
27071             Roo.each(parts, function(p) {
27072                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27073                 if (!p.length) {
27074                     return true;
27075                 }
27076                 var l = p.split(':').shift().replace(/\s+/g,'');
27077                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27078                 
27079                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27080 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27081                     //node.removeAttribute(n);
27082                     return true;
27083                 }
27084                 //Roo.log()
27085                 // only allow 'c whitelisted system attributes'
27086                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27087 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27088                     //node.removeAttribute(n);
27089                     return true;
27090                 }
27091                 
27092                 
27093                  
27094                 
27095                 clean.push(p);
27096                 return true;
27097             });
27098             if (clean.length) { 
27099                 node.setAttribute(n, clean.join(';'));
27100             } else {
27101                 node.removeAttribute(n);
27102             }
27103             
27104         }
27105         
27106         
27107         for (var i = node.attributes.length-1; i > -1 ; i--) {
27108             var a = node.attributes[i];
27109             //console.log(a);
27110             
27111             if (a.name.toLowerCase().substr(0,2)=='on')  {
27112                 node.removeAttribute(a.name);
27113                 continue;
27114             }
27115             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27116                 node.removeAttribute(a.name);
27117                 continue;
27118             }
27119             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27120                 cleanAttr(a.name,a.value); // fixme..
27121                 continue;
27122             }
27123             if (a.name == 'style') {
27124                 cleanStyle(a.name,a.value);
27125                 continue;
27126             }
27127             /// clean up MS crap..
27128             // tecnically this should be a list of valid class'es..
27129             
27130             
27131             if (a.name == 'class') {
27132                 if (a.value.match(/^Mso/)) {
27133                     node.removeAttribute('class');
27134                 }
27135                 
27136                 if (a.value.match(/^body$/)) {
27137                     node.removeAttribute('class');
27138                 }
27139                 continue;
27140             }
27141             
27142             // style cleanup!?
27143             // class cleanup?
27144             
27145         }
27146         
27147         
27148         this.cleanUpChildren(node);
27149         
27150         
27151     },
27152     
27153     /**
27154      * Clean up MS wordisms...
27155      */
27156     cleanWord : function(node)
27157     {
27158         if (!node) {
27159             this.cleanWord(this.doc.body);
27160             return;
27161         }
27162         
27163         if(
27164                 node.nodeName == 'SPAN' &&
27165                 !node.hasAttributes() &&
27166                 node.childNodes.length == 1 &&
27167                 node.firstChild.nodeName == "#text"  
27168         ) {
27169             var textNode = node.firstChild;
27170             node.removeChild(textNode);
27171             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27172                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27173             }
27174             node.parentNode.insertBefore(textNode, node);
27175             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27176                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27177             }
27178             node.parentNode.removeChild(node);
27179         }
27180         
27181         if (node.nodeName == "#text") {
27182             // clean up silly Windows -- stuff?
27183             return; 
27184         }
27185         if (node.nodeName == "#comment") {
27186             node.parentNode.removeChild(node);
27187             // clean up silly Windows -- stuff?
27188             return; 
27189         }
27190         
27191         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27192             node.parentNode.removeChild(node);
27193             return;
27194         }
27195         //Roo.log(node.tagName);
27196         // remove - but keep children..
27197         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27198             //Roo.log('-- removed');
27199             while (node.childNodes.length) {
27200                 var cn = node.childNodes[0];
27201                 node.removeChild(cn);
27202                 node.parentNode.insertBefore(cn, node);
27203                 // move node to parent - and clean it..
27204                 this.cleanWord(cn);
27205             }
27206             node.parentNode.removeChild(node);
27207             /// no need to iterate chidlren = it's got none..
27208             //this.iterateChildren(node, this.cleanWord);
27209             return;
27210         }
27211         // clean styles
27212         if (node.className.length) {
27213             
27214             var cn = node.className.split(/\W+/);
27215             var cna = [];
27216             Roo.each(cn, function(cls) {
27217                 if (cls.match(/Mso[a-zA-Z]+/)) {
27218                     return;
27219                 }
27220                 cna.push(cls);
27221             });
27222             node.className = cna.length ? cna.join(' ') : '';
27223             if (!cna.length) {
27224                 node.removeAttribute("class");
27225             }
27226         }
27227         
27228         if (node.hasAttribute("lang")) {
27229             node.removeAttribute("lang");
27230         }
27231         
27232         if (node.hasAttribute("style")) {
27233             
27234             var styles = node.getAttribute("style").split(";");
27235             var nstyle = [];
27236             Roo.each(styles, function(s) {
27237                 if (!s.match(/:/)) {
27238                     return;
27239                 }
27240                 var kv = s.split(":");
27241                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27242                     return;
27243                 }
27244                 // what ever is left... we allow.
27245                 nstyle.push(s);
27246             });
27247             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27248             if (!nstyle.length) {
27249                 node.removeAttribute('style');
27250             }
27251         }
27252         this.iterateChildren(node, this.cleanWord);
27253         
27254         
27255         
27256     },
27257     /**
27258      * iterateChildren of a Node, calling fn each time, using this as the scole..
27259      * @param {DomNode} node node to iterate children of.
27260      * @param {Function} fn method of this class to call on each item.
27261      */
27262     iterateChildren : function(node, fn)
27263     {
27264         if (!node.childNodes.length) {
27265                 return;
27266         }
27267         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27268            fn.call(this, node.childNodes[i])
27269         }
27270     },
27271     
27272     
27273     /**
27274      * cleanTableWidths.
27275      *
27276      * Quite often pasting from word etc.. results in tables with column and widths.
27277      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27278      *
27279      */
27280     cleanTableWidths : function(node)
27281     {
27282          
27283          
27284         if (!node) {
27285             this.cleanTableWidths(this.doc.body);
27286             return;
27287         }
27288         
27289         // ignore list...
27290         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27291             return; 
27292         }
27293         Roo.log(node.tagName);
27294         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27295             this.iterateChildren(node, this.cleanTableWidths);
27296             return;
27297         }
27298         if (node.hasAttribute('width')) {
27299             node.removeAttribute('width');
27300         }
27301         
27302          
27303         if (node.hasAttribute("style")) {
27304             // pretty basic...
27305             
27306             var styles = node.getAttribute("style").split(";");
27307             var nstyle = [];
27308             Roo.each(styles, function(s) {
27309                 if (!s.match(/:/)) {
27310                     return;
27311                 }
27312                 var kv = s.split(":");
27313                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27314                     return;
27315                 }
27316                 // what ever is left... we allow.
27317                 nstyle.push(s);
27318             });
27319             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27320             if (!nstyle.length) {
27321                 node.removeAttribute('style');
27322             }
27323         }
27324         
27325         this.iterateChildren(node, this.cleanTableWidths);
27326         
27327         
27328     },
27329     
27330     
27331     
27332     
27333     domToHTML : function(currentElement, depth, nopadtext) {
27334         
27335         depth = depth || 0;
27336         nopadtext = nopadtext || false;
27337     
27338         if (!currentElement) {
27339             return this.domToHTML(this.doc.body);
27340         }
27341         
27342         //Roo.log(currentElement);
27343         var j;
27344         var allText = false;
27345         var nodeName = currentElement.nodeName;
27346         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27347         
27348         if  (nodeName == '#text') {
27349             
27350             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27351         }
27352         
27353         
27354         var ret = '';
27355         if (nodeName != 'BODY') {
27356              
27357             var i = 0;
27358             // Prints the node tagName, such as <A>, <IMG>, etc
27359             if (tagName) {
27360                 var attr = [];
27361                 for(i = 0; i < currentElement.attributes.length;i++) {
27362                     // quoting?
27363                     var aname = currentElement.attributes.item(i).name;
27364                     if (!currentElement.attributes.item(i).value.length) {
27365                         continue;
27366                     }
27367                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27368                 }
27369                 
27370                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27371             } 
27372             else {
27373                 
27374                 // eack
27375             }
27376         } else {
27377             tagName = false;
27378         }
27379         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27380             return ret;
27381         }
27382         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27383             nopadtext = true;
27384         }
27385         
27386         
27387         // Traverse the tree
27388         i = 0;
27389         var currentElementChild = currentElement.childNodes.item(i);
27390         var allText = true;
27391         var innerHTML  = '';
27392         lastnode = '';
27393         while (currentElementChild) {
27394             // Formatting code (indent the tree so it looks nice on the screen)
27395             var nopad = nopadtext;
27396             if (lastnode == 'SPAN') {
27397                 nopad  = true;
27398             }
27399             // text
27400             if  (currentElementChild.nodeName == '#text') {
27401                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27402                 toadd = nopadtext ? toadd : toadd.trim();
27403                 if (!nopad && toadd.length > 80) {
27404                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27405                 }
27406                 innerHTML  += toadd;
27407                 
27408                 i++;
27409                 currentElementChild = currentElement.childNodes.item(i);
27410                 lastNode = '';
27411                 continue;
27412             }
27413             allText = false;
27414             
27415             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27416                 
27417             // Recursively traverse the tree structure of the child node
27418             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27419             lastnode = currentElementChild.nodeName;
27420             i++;
27421             currentElementChild=currentElement.childNodes.item(i);
27422         }
27423         
27424         ret += innerHTML;
27425         
27426         if (!allText) {
27427                 // The remaining code is mostly for formatting the tree
27428             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27429         }
27430         
27431         
27432         if (tagName) {
27433             ret+= "</"+tagName+">";
27434         }
27435         return ret;
27436         
27437     },
27438         
27439     applyBlacklists : function()
27440     {
27441         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27442         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27443         
27444         this.white = [];
27445         this.black = [];
27446         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27447             if (b.indexOf(tag) > -1) {
27448                 return;
27449             }
27450             this.white.push(tag);
27451             
27452         }, this);
27453         
27454         Roo.each(w, function(tag) {
27455             if (b.indexOf(tag) > -1) {
27456                 return;
27457             }
27458             if (this.white.indexOf(tag) > -1) {
27459                 return;
27460             }
27461             this.white.push(tag);
27462             
27463         }, this);
27464         
27465         
27466         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27467             if (w.indexOf(tag) > -1) {
27468                 return;
27469             }
27470             this.black.push(tag);
27471             
27472         }, this);
27473         
27474         Roo.each(b, function(tag) {
27475             if (w.indexOf(tag) > -1) {
27476                 return;
27477             }
27478             if (this.black.indexOf(tag) > -1) {
27479                 return;
27480             }
27481             this.black.push(tag);
27482             
27483         }, this);
27484         
27485         
27486         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27487         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27488         
27489         this.cwhite = [];
27490         this.cblack = [];
27491         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27492             if (b.indexOf(tag) > -1) {
27493                 return;
27494             }
27495             this.cwhite.push(tag);
27496             
27497         }, this);
27498         
27499         Roo.each(w, function(tag) {
27500             if (b.indexOf(tag) > -1) {
27501                 return;
27502             }
27503             if (this.cwhite.indexOf(tag) > -1) {
27504                 return;
27505             }
27506             this.cwhite.push(tag);
27507             
27508         }, this);
27509         
27510         
27511         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27512             if (w.indexOf(tag) > -1) {
27513                 return;
27514             }
27515             this.cblack.push(tag);
27516             
27517         }, this);
27518         
27519         Roo.each(b, function(tag) {
27520             if (w.indexOf(tag) > -1) {
27521                 return;
27522             }
27523             if (this.cblack.indexOf(tag) > -1) {
27524                 return;
27525             }
27526             this.cblack.push(tag);
27527             
27528         }, this);
27529     },
27530     
27531     setStylesheets : function(stylesheets)
27532     {
27533         if(typeof(stylesheets) == 'string'){
27534             Roo.get(this.iframe.contentDocument.head).createChild({
27535                 tag : 'link',
27536                 rel : 'stylesheet',
27537                 type : 'text/css',
27538                 href : stylesheets
27539             });
27540             
27541             return;
27542         }
27543         var _this = this;
27544      
27545         Roo.each(stylesheets, function(s) {
27546             if(!s.length){
27547                 return;
27548             }
27549             
27550             Roo.get(_this.iframe.contentDocument.head).createChild({
27551                 tag : 'link',
27552                 rel : 'stylesheet',
27553                 type : 'text/css',
27554                 href : s
27555             });
27556         });
27557
27558         
27559     },
27560     
27561     removeStylesheets : function()
27562     {
27563         var _this = this;
27564         
27565         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27566             s.remove();
27567         });
27568     },
27569     
27570     setStyle : function(style)
27571     {
27572         Roo.get(this.iframe.contentDocument.head).createChild({
27573             tag : 'style',
27574             type : 'text/css',
27575             html : style
27576         });
27577
27578         return;
27579     }
27580     
27581     // hide stuff that is not compatible
27582     /**
27583      * @event blur
27584      * @hide
27585      */
27586     /**
27587      * @event change
27588      * @hide
27589      */
27590     /**
27591      * @event focus
27592      * @hide
27593      */
27594     /**
27595      * @event specialkey
27596      * @hide
27597      */
27598     /**
27599      * @cfg {String} fieldClass @hide
27600      */
27601     /**
27602      * @cfg {String} focusClass @hide
27603      */
27604     /**
27605      * @cfg {String} autoCreate @hide
27606      */
27607     /**
27608      * @cfg {String} inputType @hide
27609      */
27610     /**
27611      * @cfg {String} invalidClass @hide
27612      */
27613     /**
27614      * @cfg {String} invalidText @hide
27615      */
27616     /**
27617      * @cfg {String} msgFx @hide
27618      */
27619     /**
27620      * @cfg {String} validateOnBlur @hide
27621      */
27622 });
27623
27624 Roo.HtmlEditorCore.white = [
27625         'area', 'br', 'img', 'input', 'hr', 'wbr',
27626         
27627        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27628        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27629        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27630        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27631        'table',   'ul',         'xmp', 
27632        
27633        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27634       'thead',   'tr', 
27635      
27636       'dir', 'menu', 'ol', 'ul', 'dl',
27637        
27638       'embed',  'object'
27639 ];
27640
27641
27642 Roo.HtmlEditorCore.black = [
27643     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27644         'applet', // 
27645         'base',   'basefont', 'bgsound', 'blink',  'body', 
27646         'frame',  'frameset', 'head',    'html',   'ilayer', 
27647         'iframe', 'layer',  'link',     'meta',    'object',   
27648         'script', 'style' ,'title',  'xml' // clean later..
27649 ];
27650 Roo.HtmlEditorCore.clean = [
27651     'script', 'style', 'title', 'xml'
27652 ];
27653 Roo.HtmlEditorCore.remove = [
27654     'font'
27655 ];
27656 // attributes..
27657
27658 Roo.HtmlEditorCore.ablack = [
27659     'on'
27660 ];
27661     
27662 Roo.HtmlEditorCore.aclean = [ 
27663     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27664 ];
27665
27666 // protocols..
27667 Roo.HtmlEditorCore.pwhite= [
27668         'http',  'https',  'mailto'
27669 ];
27670
27671 // white listed style attributes.
27672 Roo.HtmlEditorCore.cwhite= [
27673       //  'text-align', /// default is to allow most things..
27674       
27675          
27676 //        'font-size'//??
27677 ];
27678
27679 // black listed style attributes.
27680 Roo.HtmlEditorCore.cblack= [
27681       //  'font-size' -- this can be set by the project 
27682 ];
27683
27684
27685 Roo.HtmlEditorCore.swapCodes   =[ 
27686     [    8211, "&#8211;" ], 
27687     [    8212, "&#8212;" ], 
27688     [    8216,  "'" ],  
27689     [    8217, "'" ],  
27690     [    8220, '"' ],  
27691     [    8221, '"' ],  
27692     [    8226, "*" ],  
27693     [    8230, "..." ]
27694 ]; 
27695
27696     /*
27697  * - LGPL
27698  *
27699  * HtmlEditor
27700  * 
27701  */
27702
27703 /**
27704  * @class Roo.bootstrap.HtmlEditor
27705  * @extends Roo.bootstrap.TextArea
27706  * Bootstrap HtmlEditor class
27707
27708  * @constructor
27709  * Create a new HtmlEditor
27710  * @param {Object} config The config object
27711  */
27712
27713 Roo.bootstrap.HtmlEditor = function(config){
27714     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27715     if (!this.toolbars) {
27716         this.toolbars = [];
27717     }
27718     
27719     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27720     this.addEvents({
27721             /**
27722              * @event initialize
27723              * Fires when the editor is fully initialized (including the iframe)
27724              * @param {HtmlEditor} this
27725              */
27726             initialize: true,
27727             /**
27728              * @event activate
27729              * Fires when the editor is first receives the focus. Any insertion must wait
27730              * until after this event.
27731              * @param {HtmlEditor} this
27732              */
27733             activate: true,
27734              /**
27735              * @event beforesync
27736              * Fires before the textarea is updated with content from the editor iframe. Return false
27737              * to cancel the sync.
27738              * @param {HtmlEditor} this
27739              * @param {String} html
27740              */
27741             beforesync: true,
27742              /**
27743              * @event beforepush
27744              * Fires before the iframe editor is updated with content from the textarea. Return false
27745              * to cancel the push.
27746              * @param {HtmlEditor} this
27747              * @param {String} html
27748              */
27749             beforepush: true,
27750              /**
27751              * @event sync
27752              * Fires when the textarea is updated with content from the editor iframe.
27753              * @param {HtmlEditor} this
27754              * @param {String} html
27755              */
27756             sync: true,
27757              /**
27758              * @event push
27759              * Fires when the iframe editor is updated with content from the textarea.
27760              * @param {HtmlEditor} this
27761              * @param {String} html
27762              */
27763             push: true,
27764              /**
27765              * @event editmodechange
27766              * Fires when the editor switches edit modes
27767              * @param {HtmlEditor} this
27768              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27769              */
27770             editmodechange: true,
27771             /**
27772              * @event editorevent
27773              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27774              * @param {HtmlEditor} this
27775              */
27776             editorevent: true,
27777             /**
27778              * @event firstfocus
27779              * Fires when on first focus - needed by toolbars..
27780              * @param {HtmlEditor} this
27781              */
27782             firstfocus: true,
27783             /**
27784              * @event autosave
27785              * Auto save the htmlEditor value as a file into Events
27786              * @param {HtmlEditor} this
27787              */
27788             autosave: true,
27789             /**
27790              * @event savedpreview
27791              * preview the saved version of htmlEditor
27792              * @param {HtmlEditor} this
27793              */
27794             savedpreview: true
27795         });
27796 };
27797
27798
27799 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27800     
27801     
27802       /**
27803      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27804      */
27805     toolbars : false,
27806     
27807      /**
27808     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27809     */
27810     btns : [],
27811    
27812      /**
27813      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27814      *                        Roo.resizable.
27815      */
27816     resizable : false,
27817      /**
27818      * @cfg {Number} height (in pixels)
27819      */   
27820     height: 300,
27821    /**
27822      * @cfg {Number} width (in pixels)
27823      */   
27824     width: false,
27825     
27826     /**
27827      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27828      * 
27829      */
27830     stylesheets: false,
27831     
27832     // id of frame..
27833     frameId: false,
27834     
27835     // private properties
27836     validationEvent : false,
27837     deferHeight: true,
27838     initialized : false,
27839     activated : false,
27840     
27841     onFocus : Roo.emptyFn,
27842     iframePad:3,
27843     hideMode:'offsets',
27844     
27845     tbContainer : false,
27846     
27847     bodyCls : '',
27848     
27849     toolbarContainer :function() {
27850         return this.wrap.select('.x-html-editor-tb',true).first();
27851     },
27852
27853     /**
27854      * Protected method that will not generally be called directly. It
27855      * is called when the editor creates its toolbar. Override this method if you need to
27856      * add custom toolbar buttons.
27857      * @param {HtmlEditor} editor
27858      */
27859     createToolbar : function(){
27860         Roo.log('renewing');
27861         Roo.log("create toolbars");
27862         
27863         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27864         this.toolbars[0].render(this.toolbarContainer());
27865         
27866         return;
27867         
27868 //        if (!editor.toolbars || !editor.toolbars.length) {
27869 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27870 //        }
27871 //        
27872 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27873 //            editor.toolbars[i] = Roo.factory(
27874 //                    typeof(editor.toolbars[i]) == 'string' ?
27875 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27876 //                Roo.bootstrap.HtmlEditor);
27877 //            editor.toolbars[i].init(editor);
27878 //        }
27879     },
27880
27881      
27882     // private
27883     onRender : function(ct, position)
27884     {
27885        // Roo.log("Call onRender: " + this.xtype);
27886         var _t = this;
27887         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27888       
27889         this.wrap = this.inputEl().wrap({
27890             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27891         });
27892         
27893         this.editorcore.onRender(ct, position);
27894          
27895         if (this.resizable) {
27896             this.resizeEl = new Roo.Resizable(this.wrap, {
27897                 pinned : true,
27898                 wrap: true,
27899                 dynamic : true,
27900                 minHeight : this.height,
27901                 height: this.height,
27902                 handles : this.resizable,
27903                 width: this.width,
27904                 listeners : {
27905                     resize : function(r, w, h) {
27906                         _t.onResize(w,h); // -something
27907                     }
27908                 }
27909             });
27910             
27911         }
27912         this.createToolbar(this);
27913        
27914         
27915         if(!this.width && this.resizable){
27916             this.setSize(this.wrap.getSize());
27917         }
27918         if (this.resizeEl) {
27919             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27920             // should trigger onReize..
27921         }
27922         
27923     },
27924
27925     // private
27926     onResize : function(w, h)
27927     {
27928         Roo.log('resize: ' +w + ',' + h );
27929         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27930         var ew = false;
27931         var eh = false;
27932         
27933         if(this.inputEl() ){
27934             if(typeof w == 'number'){
27935                 var aw = w - this.wrap.getFrameWidth('lr');
27936                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27937                 ew = aw;
27938             }
27939             if(typeof h == 'number'){
27940                  var tbh = -11;  // fixme it needs to tool bar size!
27941                 for (var i =0; i < this.toolbars.length;i++) {
27942                     // fixme - ask toolbars for heights?
27943                     tbh += this.toolbars[i].el.getHeight();
27944                     //if (this.toolbars[i].footer) {
27945                     //    tbh += this.toolbars[i].footer.el.getHeight();
27946                     //}
27947                 }
27948               
27949                 
27950                 
27951                 
27952                 
27953                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27954                 ah -= 5; // knock a few pixes off for look..
27955                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27956                 var eh = ah;
27957             }
27958         }
27959         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27960         this.editorcore.onResize(ew,eh);
27961         
27962     },
27963
27964     /**
27965      * Toggles the editor between standard and source edit mode.
27966      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27967      */
27968     toggleSourceEdit : function(sourceEditMode)
27969     {
27970         this.editorcore.toggleSourceEdit(sourceEditMode);
27971         
27972         if(this.editorcore.sourceEditMode){
27973             Roo.log('editor - showing textarea');
27974             
27975 //            Roo.log('in');
27976 //            Roo.log(this.syncValue());
27977             this.syncValue();
27978             this.inputEl().removeClass(['hide', 'x-hidden']);
27979             this.inputEl().dom.removeAttribute('tabIndex');
27980             this.inputEl().focus();
27981         }else{
27982             Roo.log('editor - hiding textarea');
27983 //            Roo.log('out')
27984 //            Roo.log(this.pushValue()); 
27985             this.pushValue();
27986             
27987             this.inputEl().addClass(['hide', 'x-hidden']);
27988             this.inputEl().dom.setAttribute('tabIndex', -1);
27989             //this.deferFocus();
27990         }
27991          
27992         if(this.resizable){
27993             this.setSize(this.wrap.getSize());
27994         }
27995         
27996         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27997     },
27998  
27999     // private (for BoxComponent)
28000     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28001
28002     // private (for BoxComponent)
28003     getResizeEl : function(){
28004         return this.wrap;
28005     },
28006
28007     // private (for BoxComponent)
28008     getPositionEl : function(){
28009         return this.wrap;
28010     },
28011
28012     // private
28013     initEvents : function(){
28014         this.originalValue = this.getValue();
28015     },
28016
28017 //    /**
28018 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28019 //     * @method
28020 //     */
28021 //    markInvalid : Roo.emptyFn,
28022 //    /**
28023 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28024 //     * @method
28025 //     */
28026 //    clearInvalid : Roo.emptyFn,
28027
28028     setValue : function(v){
28029         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
28030         this.editorcore.pushValue();
28031     },
28032
28033      
28034     // private
28035     deferFocus : function(){
28036         this.focus.defer(10, this);
28037     },
28038
28039     // doc'ed in Field
28040     focus : function(){
28041         this.editorcore.focus();
28042         
28043     },
28044       
28045
28046     // private
28047     onDestroy : function(){
28048         
28049         
28050         
28051         if(this.rendered){
28052             
28053             for (var i =0; i < this.toolbars.length;i++) {
28054                 // fixme - ask toolbars for heights?
28055                 this.toolbars[i].onDestroy();
28056             }
28057             
28058             this.wrap.dom.innerHTML = '';
28059             this.wrap.remove();
28060         }
28061     },
28062
28063     // private
28064     onFirstFocus : function(){
28065         //Roo.log("onFirstFocus");
28066         this.editorcore.onFirstFocus();
28067          for (var i =0; i < this.toolbars.length;i++) {
28068             this.toolbars[i].onFirstFocus();
28069         }
28070         
28071     },
28072     
28073     // private
28074     syncValue : function()
28075     {   
28076         this.editorcore.syncValue();
28077     },
28078     
28079     pushValue : function()
28080     {   
28081         this.editorcore.pushValue();
28082     }
28083      
28084     
28085     // hide stuff that is not compatible
28086     /**
28087      * @event blur
28088      * @hide
28089      */
28090     /**
28091      * @event change
28092      * @hide
28093      */
28094     /**
28095      * @event focus
28096      * @hide
28097      */
28098     /**
28099      * @event specialkey
28100      * @hide
28101      */
28102     /**
28103      * @cfg {String} fieldClass @hide
28104      */
28105     /**
28106      * @cfg {String} focusClass @hide
28107      */
28108     /**
28109      * @cfg {String} autoCreate @hide
28110      */
28111     /**
28112      * @cfg {String} inputType @hide
28113      */
28114      
28115     /**
28116      * @cfg {String} invalidText @hide
28117      */
28118     /**
28119      * @cfg {String} msgFx @hide
28120      */
28121     /**
28122      * @cfg {String} validateOnBlur @hide
28123      */
28124 });
28125  
28126     
28127    
28128    
28129    
28130       
28131 Roo.namespace('Roo.bootstrap.htmleditor');
28132 /**
28133  * @class Roo.bootstrap.HtmlEditorToolbar1
28134  * @extends Roo.bootstrap.nav.Simplebar
28135  * Basic Toolbar
28136  * 
28137  * @example
28138  * Usage:
28139  *
28140  new Roo.bootstrap.HtmlEditor({
28141     ....
28142     toolbars : [
28143         new Roo.bootstrap.HtmlEditorToolbar1({
28144             disable : { fonts: 1 , format: 1, ..., ... , ...],
28145             btns : [ .... ]
28146         })
28147     }
28148      
28149  * 
28150  * @cfg {Object} disable List of elements to disable..
28151  * @cfg {Array} btns List of additional buttons.
28152  * 
28153  * 
28154  * NEEDS Extra CSS? 
28155  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28156  */
28157  
28158 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
28159 {
28160     
28161     Roo.apply(this, config);
28162     
28163     // default disabled, based on 'good practice'..
28164     this.disable = this.disable || {};
28165     Roo.applyIf(this.disable, {
28166         fontSize : true,
28167         colors : true,
28168         specialElements : true
28169     });
28170     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
28171     
28172     this.editor = config.editor;
28173     this.editorcore = config.editor.editorcore;
28174     
28175     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28176     
28177     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28178     // dont call parent... till later.
28179 }
28180 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28181      
28182     bar : true,
28183     
28184     editor : false,
28185     editorcore : false,
28186     
28187     
28188     formats : [
28189         "p" ,  
28190         "h1","h2","h3","h4","h5","h6", 
28191         "pre", "code", 
28192         "abbr", "acronym", "address", "cite", "samp", "var",
28193         'div','span'
28194     ],
28195     
28196     onRender : function(ct, position)
28197     {
28198        // Roo.log("Call onRender: " + this.xtype);
28199         
28200        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
28201        Roo.log(this.el);
28202        this.el.dom.style.marginBottom = '0';
28203        var _this = this;
28204        var editorcore = this.editorcore;
28205        var editor= this.editor;
28206        
28207        var children = [];
28208        var btn = function(id,cmd , toggle, handler, html){
28209        
28210             var  event = toggle ? 'toggle' : 'click';
28211        
28212             var a = {
28213                 size : 'sm',
28214                 xtype: 'Button',
28215                 xns: Roo.bootstrap,
28216                 //glyphicon : id,
28217                 fa: id,
28218                 cmd : id || cmd,
28219                 enableToggle:toggle !== false,
28220                 html : html || '',
28221                 pressed : toggle ? false : null,
28222                 listeners : {}
28223             };
28224             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28225                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28226             };
28227             children.push(a);
28228             return a;
28229        }
28230        
28231     //    var cb_box = function...
28232         
28233         var style = {
28234                 xtype: 'Button',
28235                 size : 'sm',
28236                 xns: Roo.bootstrap,
28237                 fa : 'font',
28238                 //html : 'submit'
28239                 menu : {
28240                     xtype: 'Menu',
28241                     xns: Roo.bootstrap,
28242                     items:  []
28243                 }
28244         };
28245         Roo.each(this.formats, function(f) {
28246             style.menu.items.push({
28247                 xtype :'MenuItem',
28248                 xns: Roo.bootstrap,
28249                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28250                 tagname : f,
28251                 listeners : {
28252                     click : function()
28253                     {
28254                         editorcore.insertTag(this.tagname);
28255                         editor.focus();
28256                     }
28257                 }
28258                 
28259             });
28260         });
28261         children.push(style);   
28262         
28263         btn('bold',false,true);
28264         btn('italic',false,true);
28265         btn('align-left', 'justifyleft',true);
28266         btn('align-center', 'justifycenter',true);
28267         btn('align-right' , 'justifyright',true);
28268         btn('link', false, false, function(btn) {
28269             //Roo.log("create link?");
28270             var url = prompt(this.createLinkText, this.defaultLinkValue);
28271             if(url && url != 'http:/'+'/'){
28272                 this.editorcore.relayCmd('createlink', url);
28273             }
28274         }),
28275         btn('list','insertunorderedlist',true);
28276         btn('pencil', false,true, function(btn){
28277                 Roo.log(this);
28278                 this.toggleSourceEdit(btn.pressed);
28279         });
28280         
28281         if (this.editor.btns.length > 0) {
28282             for (var i = 0; i<this.editor.btns.length; i++) {
28283                 children.push(this.editor.btns[i]);
28284             }
28285         }
28286         
28287         /*
28288         var cog = {
28289                 xtype: 'Button',
28290                 size : 'sm',
28291                 xns: Roo.bootstrap,
28292                 glyphicon : 'cog',
28293                 //html : 'submit'
28294                 menu : {
28295                     xtype: 'Menu',
28296                     xns: Roo.bootstrap,
28297                     items:  []
28298                 }
28299         };
28300         
28301         cog.menu.items.push({
28302             xtype :'MenuItem',
28303             xns: Roo.bootstrap,
28304             html : Clean styles,
28305             tagname : f,
28306             listeners : {
28307                 click : function()
28308                 {
28309                     editorcore.insertTag(this.tagname);
28310                     editor.focus();
28311                 }
28312             }
28313             
28314         });
28315        */
28316         
28317          
28318        this.xtype = 'NavSimplebar';
28319         
28320         for(var i=0;i< children.length;i++) {
28321             
28322             this.buttons.add(this.addxtypeChild(children[i]));
28323             
28324         }
28325         
28326         editor.on('editorevent', this.updateToolbar, this);
28327     },
28328     onBtnClick : function(id)
28329     {
28330        this.editorcore.relayCmd(id);
28331        this.editorcore.focus();
28332     },
28333     
28334     /**
28335      * Protected method that will not generally be called directly. It triggers
28336      * a toolbar update by reading the markup state of the current selection in the editor.
28337      */
28338     updateToolbar: function(){
28339
28340         if(!this.editorcore.activated){
28341             this.editor.onFirstFocus(); // is this neeed?
28342             return;
28343         }
28344
28345         var btns = this.buttons; 
28346         var doc = this.editorcore.doc;
28347         btns.get('bold').setActive(doc.queryCommandState('bold'));
28348         btns.get('italic').setActive(doc.queryCommandState('italic'));
28349         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28350         
28351         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28352         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28353         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28354         
28355         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28356         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28357          /*
28358         
28359         var ans = this.editorcore.getAllAncestors();
28360         if (this.formatCombo) {
28361             
28362             
28363             var store = this.formatCombo.store;
28364             this.formatCombo.setValue("");
28365             for (var i =0; i < ans.length;i++) {
28366                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28367                     // select it..
28368                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28369                     break;
28370                 }
28371             }
28372         }
28373         
28374         
28375         
28376         // hides menus... - so this cant be on a menu...
28377         Roo.bootstrap.MenuMgr.hideAll();
28378         */
28379         Roo.bootstrap.menu.Manager.hideAll();
28380         //this.editorsyncValue();
28381     },
28382     onFirstFocus: function() {
28383         this.buttons.each(function(item){
28384            item.enable();
28385         });
28386     },
28387     toggleSourceEdit : function(sourceEditMode){
28388         
28389           
28390         if(sourceEditMode){
28391             Roo.log("disabling buttons");
28392            this.buttons.each( function(item){
28393                 if(item.cmd != 'pencil'){
28394                     item.disable();
28395                 }
28396             });
28397           
28398         }else{
28399             Roo.log("enabling buttons");
28400             if(this.editorcore.initialized){
28401                 this.buttons.each( function(item){
28402                     item.enable();
28403                 });
28404             }
28405             
28406         }
28407         Roo.log("calling toggole on editor");
28408         // tell the editor that it's been pressed..
28409         this.editor.toggleSourceEdit(sourceEditMode);
28410        
28411     }
28412 });
28413
28414
28415
28416
28417  
28418 /*
28419  * - LGPL
28420  */
28421
28422 /**
28423  * @class Roo.bootstrap.Markdown
28424  * @extends Roo.bootstrap.TextArea
28425  * Bootstrap Showdown editable area
28426  * @cfg {string} content
28427  * 
28428  * @constructor
28429  * Create a new Showdown
28430  */
28431
28432 Roo.bootstrap.Markdown = function(config){
28433     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28434    
28435 };
28436
28437 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28438     
28439     editing :false,
28440     
28441     initEvents : function()
28442     {
28443         
28444         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28445         this.markdownEl = this.el.createChild({
28446             cls : 'roo-markdown-area'
28447         });
28448         this.inputEl().addClass('d-none');
28449         if (this.getValue() == '') {
28450             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28451             
28452         } else {
28453             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28454         }
28455         this.markdownEl.on('click', this.toggleTextEdit, this);
28456         this.on('blur', this.toggleTextEdit, this);
28457         this.on('specialkey', this.resizeTextArea, this);
28458     },
28459     
28460     toggleTextEdit : function()
28461     {
28462         var sh = this.markdownEl.getHeight();
28463         this.inputEl().addClass('d-none');
28464         this.markdownEl.addClass('d-none');
28465         if (!this.editing) {
28466             // show editor?
28467             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28468             this.inputEl().removeClass('d-none');
28469             this.inputEl().focus();
28470             this.editing = true;
28471             return;
28472         }
28473         // show showdown...
28474         this.updateMarkdown();
28475         this.markdownEl.removeClass('d-none');
28476         this.editing = false;
28477         return;
28478     },
28479     updateMarkdown : function()
28480     {
28481         if (this.getValue() == '') {
28482             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28483             return;
28484         }
28485  
28486         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28487     },
28488     
28489     resizeTextArea: function () {
28490         
28491         var sh = 100;
28492         Roo.log([sh, this.getValue().split("\n").length * 30]);
28493         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28494     },
28495     setValue : function(val)
28496     {
28497         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28498         if (!this.editing) {
28499             this.updateMarkdown();
28500         }
28501         
28502     },
28503     focus : function()
28504     {
28505         if (!this.editing) {
28506             this.toggleTextEdit();
28507         }
28508         
28509     }
28510
28511
28512 });/*
28513  * Based on:
28514  * Ext JS Library 1.1.1
28515  * Copyright(c) 2006-2007, Ext JS, LLC.
28516  *
28517  * Originally Released Under LGPL - original licence link has changed is not relivant.
28518  *
28519  * Fork - LGPL
28520  * <script type="text/javascript">
28521  */
28522  
28523 /**
28524  * @class Roo.bootstrap.PagingToolbar
28525  * @extends Roo.bootstrap.nav.Simplebar
28526  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28527  * @constructor
28528  * Create a new PagingToolbar
28529  * @param {Object} config The config object
28530  * @param {Roo.data.Store} store
28531  */
28532 Roo.bootstrap.PagingToolbar = function(config)
28533 {
28534     // old args format still supported... - xtype is prefered..
28535         // created from xtype...
28536     
28537     this.ds = config.dataSource;
28538     
28539     if (config.store && !this.ds) {
28540         this.store= Roo.factory(config.store, Roo.data);
28541         this.ds = this.store;
28542         this.ds.xmodule = this.xmodule || false;
28543     }
28544     
28545     this.toolbarItems = [];
28546     if (config.items) {
28547         this.toolbarItems = config.items;
28548     }
28549     
28550     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28551     
28552     this.cursor = 0;
28553     
28554     if (this.ds) { 
28555         this.bind(this.ds);
28556     }
28557     
28558     if (Roo.bootstrap.version == 4) {
28559         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28560     } else {
28561         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28562     }
28563     
28564 };
28565
28566 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28567     /**
28568      * @cfg {Roo.bootstrap.Button} buttons[]
28569      * Buttons for the toolbar
28570      */
28571      /**
28572      * @cfg {Roo.data.Store} store
28573      * The underlying data store providing the paged data
28574      */
28575     /**
28576      * @cfg {String/HTMLElement/Element} container
28577      * container The id or element that will contain the toolbar
28578      */
28579     /**
28580      * @cfg {Boolean} displayInfo
28581      * True to display the displayMsg (defaults to false)
28582      */
28583     /**
28584      * @cfg {Number} pageSize
28585      * The number of records to display per page (defaults to 20)
28586      */
28587     pageSize: 20,
28588     /**
28589      * @cfg {String} displayMsg
28590      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28591      */
28592     displayMsg : 'Displaying {0} - {1} of {2}',
28593     /**
28594      * @cfg {String} emptyMsg
28595      * The message to display when no records are found (defaults to "No data to display")
28596      */
28597     emptyMsg : 'No data to display',
28598     /**
28599      * Customizable piece of the default paging text (defaults to "Page")
28600      * @type String
28601      */
28602     beforePageText : "Page",
28603     /**
28604      * Customizable piece of the default paging text (defaults to "of %0")
28605      * @type String
28606      */
28607     afterPageText : "of {0}",
28608     /**
28609      * Customizable piece of the default paging text (defaults to "First Page")
28610      * @type String
28611      */
28612     firstText : "First Page",
28613     /**
28614      * Customizable piece of the default paging text (defaults to "Previous Page")
28615      * @type String
28616      */
28617     prevText : "Previous Page",
28618     /**
28619      * Customizable piece of the default paging text (defaults to "Next Page")
28620      * @type String
28621      */
28622     nextText : "Next Page",
28623     /**
28624      * Customizable piece of the default paging text (defaults to "Last Page")
28625      * @type String
28626      */
28627     lastText : "Last Page",
28628     /**
28629      * Customizable piece of the default paging text (defaults to "Refresh")
28630      * @type String
28631      */
28632     refreshText : "Refresh",
28633
28634     buttons : false,
28635     // private
28636     onRender : function(ct, position) 
28637     {
28638         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28639         this.navgroup.parentId = this.id;
28640         this.navgroup.onRender(this.el, null);
28641         // add the buttons to the navgroup
28642         
28643         if(this.displayInfo){
28644             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28645             this.displayEl = this.el.select('.x-paging-info', true).first();
28646 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28647 //            this.displayEl = navel.el.select('span',true).first();
28648         }
28649         
28650         var _this = this;
28651         
28652         if(this.buttons){
28653             Roo.each(_this.buttons, function(e){ // this might need to use render????
28654                Roo.factory(e).render(_this.el);
28655             });
28656         }
28657             
28658         Roo.each(_this.toolbarItems, function(e) {
28659             _this.navgroup.addItem(e);
28660         });
28661         
28662         
28663         this.first = this.navgroup.addItem({
28664             tooltip: this.firstText,
28665             cls: "prev btn-outline-secondary",
28666             html : ' <i class="fa fa-step-backward"></i>',
28667             disabled: true,
28668             preventDefault: true,
28669             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28670         });
28671         
28672         this.prev =  this.navgroup.addItem({
28673             tooltip: this.prevText,
28674             cls: "prev btn-outline-secondary",
28675             html : ' <i class="fa fa-backward"></i>',
28676             disabled: true,
28677             preventDefault: true,
28678             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28679         });
28680     //this.addSeparator();
28681         
28682         
28683         var field = this.navgroup.addItem( {
28684             tagtype : 'span',
28685             cls : 'x-paging-position  btn-outline-secondary',
28686              disabled: true,
28687             html : this.beforePageText  +
28688                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28689                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28690          } ); //?? escaped?
28691         
28692         this.field = field.el.select('input', true).first();
28693         this.field.on("keydown", this.onPagingKeydown, this);
28694         this.field.on("focus", function(){this.dom.select();});
28695     
28696     
28697         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28698         //this.field.setHeight(18);
28699         //this.addSeparator();
28700         this.next = this.navgroup.addItem({
28701             tooltip: this.nextText,
28702             cls: "next btn-outline-secondary",
28703             html : ' <i class="fa fa-forward"></i>',
28704             disabled: true,
28705             preventDefault: true,
28706             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28707         });
28708         this.last = this.navgroup.addItem({
28709             tooltip: this.lastText,
28710             html : ' <i class="fa fa-step-forward"></i>',
28711             cls: "next btn-outline-secondary",
28712             disabled: true,
28713             preventDefault: true,
28714             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28715         });
28716     //this.addSeparator();
28717         this.loading = this.navgroup.addItem({
28718             tooltip: this.refreshText,
28719             cls: "btn-outline-secondary",
28720             html : ' <i class="fa fa-refresh"></i>',
28721             preventDefault: true,
28722             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28723         });
28724         
28725     },
28726
28727     // private
28728     updateInfo : function(){
28729         if(this.displayEl){
28730             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28731             var msg = count == 0 ?
28732                 this.emptyMsg :
28733                 String.format(
28734                     this.displayMsg,
28735                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28736                 );
28737             this.displayEl.update(msg);
28738         }
28739     },
28740
28741     // private
28742     onLoad : function(ds, r, o)
28743     {
28744         this.cursor = o.params && o.params.start ? o.params.start : 0;
28745         
28746         var d = this.getPageData(),
28747             ap = d.activePage,
28748             ps = d.pages;
28749         
28750         
28751         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28752         this.field.dom.value = ap;
28753         this.first.setDisabled(ap == 1);
28754         this.prev.setDisabled(ap == 1);
28755         this.next.setDisabled(ap == ps);
28756         this.last.setDisabled(ap == ps);
28757         this.loading.enable();
28758         this.updateInfo();
28759     },
28760
28761     // private
28762     getPageData : function(){
28763         var total = this.ds.getTotalCount();
28764         return {
28765             total : total,
28766             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28767             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28768         };
28769     },
28770
28771     // private
28772     onLoadError : function(){
28773         this.loading.enable();
28774     },
28775
28776     // private
28777     onPagingKeydown : function(e){
28778         var k = e.getKey();
28779         var d = this.getPageData();
28780         if(k == e.RETURN){
28781             var v = this.field.dom.value, pageNum;
28782             if(!v || isNaN(pageNum = parseInt(v, 10))){
28783                 this.field.dom.value = d.activePage;
28784                 return;
28785             }
28786             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28787             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28788             e.stopEvent();
28789         }
28790         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))
28791         {
28792           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28793           this.field.dom.value = pageNum;
28794           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28795           e.stopEvent();
28796         }
28797         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28798         {
28799           var v = this.field.dom.value, pageNum; 
28800           var increment = (e.shiftKey) ? 10 : 1;
28801           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28802                 increment *= -1;
28803           }
28804           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28805             this.field.dom.value = d.activePage;
28806             return;
28807           }
28808           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28809           {
28810             this.field.dom.value = parseInt(v, 10) + increment;
28811             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28812             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28813           }
28814           e.stopEvent();
28815         }
28816     },
28817
28818     // private
28819     beforeLoad : function(){
28820         if(this.loading){
28821             this.loading.disable();
28822         }
28823     },
28824
28825     // private
28826     onClick : function(which){
28827         
28828         var ds = this.ds;
28829         if (!ds) {
28830             return;
28831         }
28832         
28833         switch(which){
28834             case "first":
28835                 ds.load({params:{start: 0, limit: this.pageSize}});
28836             break;
28837             case "prev":
28838                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28839             break;
28840             case "next":
28841                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28842             break;
28843             case "last":
28844                 var total = ds.getTotalCount();
28845                 var extra = total % this.pageSize;
28846                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28847                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28848             break;
28849             case "refresh":
28850                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28851             break;
28852         }
28853     },
28854
28855     /**
28856      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28857      * @param {Roo.data.Store} store The data store to unbind
28858      */
28859     unbind : function(ds){
28860         ds.un("beforeload", this.beforeLoad, this);
28861         ds.un("load", this.onLoad, this);
28862         ds.un("loadexception", this.onLoadError, this);
28863         ds.un("remove", this.updateInfo, this);
28864         ds.un("add", this.updateInfo, this);
28865         this.ds = undefined;
28866     },
28867
28868     /**
28869      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28870      * @param {Roo.data.Store} store The data store to bind
28871      */
28872     bind : function(ds){
28873         ds.on("beforeload", this.beforeLoad, this);
28874         ds.on("load", this.onLoad, this);
28875         ds.on("loadexception", this.onLoadError, this);
28876         ds.on("remove", this.updateInfo, this);
28877         ds.on("add", this.updateInfo, this);
28878         this.ds = ds;
28879     }
28880 });/*
28881  * - LGPL
28882  *
28883  * element
28884  * 
28885  */
28886
28887 /**
28888  * @class Roo.bootstrap.MessageBar
28889  * @extends Roo.bootstrap.Component
28890  * Bootstrap MessageBar class
28891  * @cfg {String} html contents of the MessageBar
28892  * @cfg {String} weight (info | success | warning | danger) default info
28893  * @cfg {String} beforeClass insert the bar before the given class
28894  * @cfg {Boolean} closable (true | false) default false
28895  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28896  * 
28897  * @constructor
28898  * Create a new Element
28899  * @param {Object} config The config object
28900  */
28901
28902 Roo.bootstrap.MessageBar = function(config){
28903     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28904 };
28905
28906 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28907     
28908     html: '',
28909     weight: 'info',
28910     closable: false,
28911     fixed: false,
28912     beforeClass: 'bootstrap-sticky-wrap',
28913     
28914     getAutoCreate : function(){
28915         
28916         var cfg = {
28917             tag: 'div',
28918             cls: 'alert alert-dismissable alert-' + this.weight,
28919             cn: [
28920                 {
28921                     tag: 'span',
28922                     cls: 'message',
28923                     html: this.html || ''
28924                 }
28925             ]
28926         };
28927         
28928         if(this.fixed){
28929             cfg.cls += ' alert-messages-fixed';
28930         }
28931         
28932         if(this.closable){
28933             cfg.cn.push({
28934                 tag: 'button',
28935                 cls: 'close',
28936                 html: 'x'
28937             });
28938         }
28939         
28940         return cfg;
28941     },
28942     
28943     onRender : function(ct, position)
28944     {
28945         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28946         
28947         if(!this.el){
28948             var cfg = Roo.apply({},  this.getAutoCreate());
28949             cfg.id = Roo.id();
28950             
28951             if (this.cls) {
28952                 cfg.cls += ' ' + this.cls;
28953             }
28954             if (this.style) {
28955                 cfg.style = this.style;
28956             }
28957             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28958             
28959             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28960         }
28961         
28962         this.el.select('>button.close').on('click', this.hide, this);
28963         
28964     },
28965     
28966     show : function()
28967     {
28968         if (!this.rendered) {
28969             this.render();
28970         }
28971         
28972         this.el.show();
28973         
28974         this.fireEvent('show', this);
28975         
28976     },
28977     
28978     hide : function()
28979     {
28980         if (!this.rendered) {
28981             this.render();
28982         }
28983         
28984         this.el.hide();
28985         
28986         this.fireEvent('hide', this);
28987     },
28988     
28989     update : function()
28990     {
28991 //        var e = this.el.dom.firstChild;
28992 //        
28993 //        if(this.closable){
28994 //            e = e.nextSibling;
28995 //        }
28996 //        
28997 //        e.data = this.html || '';
28998
28999         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29000     }
29001    
29002 });
29003
29004  
29005
29006      /*
29007  * - LGPL
29008  *
29009  * Graph
29010  * 
29011  */
29012
29013
29014 /**
29015  * @class Roo.bootstrap.Graph
29016  * @extends Roo.bootstrap.Component
29017  * Bootstrap Graph class
29018 > Prameters
29019  -sm {number} sm 4
29020  -md {number} md 5
29021  @cfg {String} graphtype  bar | vbar | pie
29022  @cfg {number} g_x coodinator | centre x (pie)
29023  @cfg {number} g_y coodinator | centre y (pie)
29024  @cfg {number} g_r radius (pie)
29025  @cfg {number} g_height height of the chart (respected by all elements in the set)
29026  @cfg {number} g_width width of the chart (respected by all elements in the set)
29027  @cfg {Object} title The title of the chart
29028     
29029  -{Array}  values
29030  -opts (object) options for the chart 
29031      o {
29032      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29033      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29034      o vgutter (number)
29035      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.
29036      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29037      o to
29038      o stretch (boolean)
29039      o }
29040  -opts (object) options for the pie
29041      o{
29042      o cut
29043      o startAngle (number)
29044      o endAngle (number)
29045      } 
29046  *
29047  * @constructor
29048  * Create a new Input
29049  * @param {Object} config The config object
29050  */
29051
29052 Roo.bootstrap.Graph = function(config){
29053     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29054     
29055     this.addEvents({
29056         // img events
29057         /**
29058          * @event click
29059          * The img click event for the img.
29060          * @param {Roo.EventObject} e
29061          */
29062         "click" : true
29063     });
29064 };
29065
29066 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29067     
29068     sm: 4,
29069     md: 5,
29070     graphtype: 'bar',
29071     g_height: 250,
29072     g_width: 400,
29073     g_x: 50,
29074     g_y: 50,
29075     g_r: 30,
29076     opts:{
29077         //g_colors: this.colors,
29078         g_type: 'soft',
29079         g_gutter: '20%'
29080
29081     },
29082     title : false,
29083
29084     getAutoCreate : function(){
29085         
29086         var cfg = {
29087             tag: 'div',
29088             html : null
29089         };
29090         
29091         
29092         return  cfg;
29093     },
29094
29095     onRender : function(ct,position){
29096         
29097         
29098         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29099         
29100         if (typeof(Raphael) == 'undefined') {
29101             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29102             return;
29103         }
29104         
29105         this.raphael = Raphael(this.el.dom);
29106         
29107                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29110                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29111                 /*
29112                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29113                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29114                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29115                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29116                 
29117                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29118                 r.barchart(330, 10, 300, 220, data1);
29119                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29120                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29121                 */
29122                 
29123                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29124                 // r.barchart(30, 30, 560, 250,  xdata, {
29125                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29126                 //     axis : "0 0 1 1",
29127                 //     axisxlabels :  xdata
29128                 //     //yvalues : cols,
29129                    
29130                 // });
29131 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29132 //        
29133 //        this.load(null,xdata,{
29134 //                axis : "0 0 1 1",
29135 //                axisxlabels :  xdata
29136 //                });
29137
29138     },
29139
29140     load : function(graphtype,xdata,opts)
29141     {
29142         this.raphael.clear();
29143         if(!graphtype) {
29144             graphtype = this.graphtype;
29145         }
29146         if(!opts){
29147             opts = this.opts;
29148         }
29149         var r = this.raphael,
29150             fin = function () {
29151                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29152             },
29153             fout = function () {
29154                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29155             },
29156             pfin = function() {
29157                 this.sector.stop();
29158                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29159
29160                 if (this.label) {
29161                     this.label[0].stop();
29162                     this.label[0].attr({ r: 7.5 });
29163                     this.label[1].attr({ "font-weight": 800 });
29164                 }
29165             },
29166             pfout = function() {
29167                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29168
29169                 if (this.label) {
29170                     this.label[0].animate({ r: 5 }, 500, "bounce");
29171                     this.label[1].attr({ "font-weight": 400 });
29172                 }
29173             };
29174
29175         switch(graphtype){
29176             case 'bar':
29177                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29178                 break;
29179             case 'hbar':
29180                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29181                 break;
29182             case 'pie':
29183 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29184 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29185 //            
29186                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29187                 
29188                 break;
29189
29190         }
29191         
29192         if(this.title){
29193             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29194         }
29195         
29196     },
29197     
29198     setTitle: function(o)
29199     {
29200         this.title = o;
29201     },
29202     
29203     initEvents: function() {
29204         
29205         if(!this.href){
29206             this.el.on('click', this.onClick, this);
29207         }
29208     },
29209     
29210     onClick : function(e)
29211     {
29212         Roo.log('img onclick');
29213         this.fireEvent('click', this, e);
29214     }
29215    
29216 });
29217
29218  
29219 /*
29220  * - LGPL
29221  *
29222  * numberBox
29223  * 
29224  */
29225 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29226
29227 /**
29228  * @class Roo.bootstrap.dash.NumberBox
29229  * @extends Roo.bootstrap.Component
29230  * Bootstrap NumberBox class
29231  * @cfg {String} headline Box headline
29232  * @cfg {String} content Box content
29233  * @cfg {String} icon Box icon
29234  * @cfg {String} footer Footer text
29235  * @cfg {String} fhref Footer href
29236  * 
29237  * @constructor
29238  * Create a new NumberBox
29239  * @param {Object} config The config object
29240  */
29241
29242
29243 Roo.bootstrap.dash.NumberBox = function(config){
29244     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29245     
29246 };
29247
29248 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29249     
29250     headline : '',
29251     content : '',
29252     icon : '',
29253     footer : '',
29254     fhref : '',
29255     ficon : '',
29256     
29257     getAutoCreate : function(){
29258         
29259         var cfg = {
29260             tag : 'div',
29261             cls : 'small-box ',
29262             cn : [
29263                 {
29264                     tag : 'div',
29265                     cls : 'inner',
29266                     cn :[
29267                         {
29268                             tag : 'h3',
29269                             cls : 'roo-headline',
29270                             html : this.headline
29271                         },
29272                         {
29273                             tag : 'p',
29274                             cls : 'roo-content',
29275                             html : this.content
29276                         }
29277                     ]
29278                 }
29279             ]
29280         };
29281         
29282         if(this.icon){
29283             cfg.cn.push({
29284                 tag : 'div',
29285                 cls : 'icon',
29286                 cn :[
29287                     {
29288                         tag : 'i',
29289                         cls : 'ion ' + this.icon
29290                     }
29291                 ]
29292             });
29293         }
29294         
29295         if(this.footer){
29296             var footer = {
29297                 tag : 'a',
29298                 cls : 'small-box-footer',
29299                 href : this.fhref || '#',
29300                 html : this.footer
29301             };
29302             
29303             cfg.cn.push(footer);
29304             
29305         }
29306         
29307         return  cfg;
29308     },
29309
29310     onRender : function(ct,position){
29311         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29312
29313
29314        
29315                 
29316     },
29317
29318     setHeadline: function (value)
29319     {
29320         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29321     },
29322     
29323     setFooter: function (value, href)
29324     {
29325         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29326         
29327         if(href){
29328             this.el.select('a.small-box-footer',true).first().attr('href', href);
29329         }
29330         
29331     },
29332
29333     setContent: function (value)
29334     {
29335         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29336     },
29337
29338     initEvents: function() 
29339     {   
29340         
29341     }
29342     
29343 });
29344
29345  
29346 /*
29347  * - LGPL
29348  *
29349  * TabBox
29350  * 
29351  */
29352 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29353
29354 /**
29355  * @class Roo.bootstrap.dash.TabBox
29356  * @extends Roo.bootstrap.Component
29357  * @children Roo.bootstrap.dash.TabPane
29358  * Bootstrap TabBox class
29359  * @cfg {String} title Title of the TabBox
29360  * @cfg {String} icon Icon of the TabBox
29361  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29362  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29363  * 
29364  * @constructor
29365  * Create a new TabBox
29366  * @param {Object} config The config object
29367  */
29368
29369
29370 Roo.bootstrap.dash.TabBox = function(config){
29371     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29372     this.addEvents({
29373         // raw events
29374         /**
29375          * @event addpane
29376          * When a pane is added
29377          * @param {Roo.bootstrap.dash.TabPane} pane
29378          */
29379         "addpane" : true,
29380         /**
29381          * @event activatepane
29382          * When a pane is activated
29383          * @param {Roo.bootstrap.dash.TabPane} pane
29384          */
29385         "activatepane" : true
29386         
29387          
29388     });
29389     
29390     this.panes = [];
29391 };
29392
29393 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29394
29395     title : '',
29396     icon : false,
29397     showtabs : true,
29398     tabScrollable : false,
29399     
29400     getChildContainer : function()
29401     {
29402         return this.el.select('.tab-content', true).first();
29403     },
29404     
29405     getAutoCreate : function(){
29406         
29407         var header = {
29408             tag: 'li',
29409             cls: 'pull-left header',
29410             html: this.title,
29411             cn : []
29412         };
29413         
29414         if(this.icon){
29415             header.cn.push({
29416                 tag: 'i',
29417                 cls: 'fa ' + this.icon
29418             });
29419         }
29420         
29421         var h = {
29422             tag: 'ul',
29423             cls: 'nav nav-tabs pull-right',
29424             cn: [
29425                 header
29426             ]
29427         };
29428         
29429         if(this.tabScrollable){
29430             h = {
29431                 tag: 'div',
29432                 cls: 'tab-header',
29433                 cn: [
29434                     {
29435                         tag: 'ul',
29436                         cls: 'nav nav-tabs pull-right',
29437                         cn: [
29438                             header
29439                         ]
29440                     }
29441                 ]
29442             };
29443         }
29444         
29445         var cfg = {
29446             tag: 'div',
29447             cls: 'nav-tabs-custom',
29448             cn: [
29449                 h,
29450                 {
29451                     tag: 'div',
29452                     cls: 'tab-content no-padding',
29453                     cn: []
29454                 }
29455             ]
29456         };
29457
29458         return  cfg;
29459     },
29460     initEvents : function()
29461     {
29462         //Roo.log('add add pane handler');
29463         this.on('addpane', this.onAddPane, this);
29464     },
29465      /**
29466      * Updates the box title
29467      * @param {String} html to set the title to.
29468      */
29469     setTitle : function(value)
29470     {
29471         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29472     },
29473     onAddPane : function(pane)
29474     {
29475         this.panes.push(pane);
29476         //Roo.log('addpane');
29477         //Roo.log(pane);
29478         // tabs are rendere left to right..
29479         if(!this.showtabs){
29480             return;
29481         }
29482         
29483         var ctr = this.el.select('.nav-tabs', true).first();
29484          
29485          
29486         var existing = ctr.select('.nav-tab',true);
29487         var qty = existing.getCount();;
29488         
29489         
29490         var tab = ctr.createChild({
29491             tag : 'li',
29492             cls : 'nav-tab' + (qty ? '' : ' active'),
29493             cn : [
29494                 {
29495                     tag : 'a',
29496                     href:'#',
29497                     html : pane.title
29498                 }
29499             ]
29500         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29501         pane.tab = tab;
29502         
29503         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29504         if (!qty) {
29505             pane.el.addClass('active');
29506         }
29507         
29508                 
29509     },
29510     onTabClick : function(ev,un,ob,pane)
29511     {
29512         //Roo.log('tab - prev default');
29513         ev.preventDefault();
29514         
29515         
29516         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29517         pane.tab.addClass('active');
29518         //Roo.log(pane.title);
29519         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29520         // technically we should have a deactivate event.. but maybe add later.
29521         // and it should not de-activate the selected tab...
29522         this.fireEvent('activatepane', pane);
29523         pane.el.addClass('active');
29524         pane.fireEvent('activate');
29525         
29526         
29527     },
29528     
29529     getActivePane : function()
29530     {
29531         var r = false;
29532         Roo.each(this.panes, function(p) {
29533             if(p.el.hasClass('active')){
29534                 r = p;
29535                 return false;
29536             }
29537             
29538             return;
29539         });
29540         
29541         return r;
29542     }
29543     
29544     
29545 });
29546
29547  
29548 /*
29549  * - LGPL
29550  *
29551  * Tab pane
29552  * 
29553  */
29554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29555 /**
29556  * @class Roo.bootstrap.TabPane
29557  * @extends Roo.bootstrap.Component
29558  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29559  * Bootstrap TabPane class
29560  * @cfg {Boolean} active (false | true) Default false
29561  * @cfg {String} title title of panel
29562
29563  * 
29564  * @constructor
29565  * Create a new TabPane
29566  * @param {Object} config The config object
29567  */
29568
29569 Roo.bootstrap.dash.TabPane = function(config){
29570     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29571     
29572     this.addEvents({
29573         // raw events
29574         /**
29575          * @event activate
29576          * When a pane is activated
29577          * @param {Roo.bootstrap.dash.TabPane} pane
29578          */
29579         "activate" : true
29580          
29581     });
29582 };
29583
29584 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29585     
29586     active : false,
29587     title : '',
29588     
29589     // the tabBox that this is attached to.
29590     tab : false,
29591      
29592     getAutoCreate : function() 
29593     {
29594         var cfg = {
29595             tag: 'div',
29596             cls: 'tab-pane'
29597         };
29598         
29599         if(this.active){
29600             cfg.cls += ' active';
29601         }
29602         
29603         return cfg;
29604     },
29605     initEvents  : function()
29606     {
29607         //Roo.log('trigger add pane handler');
29608         this.parent().fireEvent('addpane', this)
29609     },
29610     
29611      /**
29612      * Updates the tab title 
29613      * @param {String} html to set the title to.
29614      */
29615     setTitle: function(str)
29616     {
29617         if (!this.tab) {
29618             return;
29619         }
29620         this.title = str;
29621         this.tab.select('a', true).first().dom.innerHTML = str;
29622         
29623     }
29624     
29625     
29626     
29627 });
29628
29629  
29630
29631
29632  /*
29633  * - LGPL
29634  *
29635  * Tooltip
29636  * 
29637  */
29638
29639 /**
29640  * @class Roo.bootstrap.Tooltip
29641  * Bootstrap Tooltip class
29642  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29643  * to determine which dom element triggers the tooltip.
29644  * 
29645  * It needs to add support for additional attributes like tooltip-position
29646  * 
29647  * @constructor
29648  * Create a new Toolti
29649  * @param {Object} config The config object
29650  */
29651
29652 Roo.bootstrap.Tooltip = function(config){
29653     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29654     
29655     this.alignment = Roo.bootstrap.Tooltip.alignment;
29656     
29657     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29658         this.alignment = config.alignment;
29659     }
29660     
29661 };
29662
29663 Roo.apply(Roo.bootstrap.Tooltip, {
29664     /**
29665      * @function init initialize tooltip monitoring.
29666      * @static
29667      */
29668     currentEl : false,
29669     currentTip : false,
29670     currentRegion : false,
29671     
29672     //  init : delay?
29673     
29674     init : function()
29675     {
29676         Roo.get(document).on('mouseover', this.enter ,this);
29677         Roo.get(document).on('mouseout', this.leave, this);
29678          
29679         
29680         this.currentTip = new Roo.bootstrap.Tooltip();
29681     },
29682     
29683     enter : function(ev)
29684     {
29685         var dom = ev.getTarget();
29686         
29687         //Roo.log(['enter',dom]);
29688         var el = Roo.fly(dom);
29689         if (this.currentEl) {
29690             //Roo.log(dom);
29691             //Roo.log(this.currentEl);
29692             //Roo.log(this.currentEl.contains(dom));
29693             if (this.currentEl == el) {
29694                 return;
29695             }
29696             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29697                 return;
29698             }
29699
29700         }
29701         
29702         if (this.currentTip.el) {
29703             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29704         }    
29705         //Roo.log(ev);
29706         
29707         if(!el || el.dom == document){
29708             return;
29709         }
29710         
29711         var bindEl = el; 
29712         var pel = false;
29713         if (!el.attr('tooltip')) {
29714             pel = el.findParent("[tooltip]");
29715             if (pel) {
29716                 bindEl = Roo.get(pel);
29717             }
29718         }
29719         
29720        
29721         
29722         // you can not look for children, as if el is the body.. then everythign is the child..
29723         if (!pel && !el.attr('tooltip')) { //
29724             if (!el.select("[tooltip]").elements.length) {
29725                 return;
29726             }
29727             // is the mouse over this child...?
29728             bindEl = el.select("[tooltip]").first();
29729             var xy = ev.getXY();
29730             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29731                 //Roo.log("not in region.");
29732                 return;
29733             }
29734             //Roo.log("child element over..");
29735             
29736         }
29737         this.currentEl = el;
29738         this.currentTip.bind(bindEl);
29739         this.currentRegion = Roo.lib.Region.getRegion(dom);
29740         this.currentTip.enter();
29741         
29742     },
29743     leave : function(ev)
29744     {
29745         var dom = ev.getTarget();
29746         //Roo.log(['leave',dom]);
29747         if (!this.currentEl) {
29748             return;
29749         }
29750         
29751         
29752         if (dom != this.currentEl.dom) {
29753             return;
29754         }
29755         var xy = ev.getXY();
29756         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29757             return;
29758         }
29759         // only activate leave if mouse cursor is outside... bounding box..
29760         
29761         
29762         
29763         
29764         if (this.currentTip) {
29765             this.currentTip.leave();
29766         }
29767         //Roo.log('clear currentEl');
29768         this.currentEl = false;
29769         
29770         
29771     },
29772     alignment : {
29773         'left' : ['r-l', [-2,0], 'right'],
29774         'right' : ['l-r', [2,0], 'left'],
29775         'bottom' : ['t-b', [0,2], 'top'],
29776         'top' : [ 'b-t', [0,-2], 'bottom']
29777     }
29778     
29779 });
29780
29781
29782 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29783     
29784     
29785     bindEl : false,
29786     
29787     delay : null, // can be { show : 300 , hide: 500}
29788     
29789     timeout : null,
29790     
29791     hoverState : null, //???
29792     
29793     placement : 'bottom', 
29794     
29795     alignment : false,
29796     
29797     getAutoCreate : function(){
29798     
29799         var cfg = {
29800            cls : 'tooltip',   
29801            role : 'tooltip',
29802            cn : [
29803                 {
29804                     cls : 'tooltip-arrow arrow'
29805                 },
29806                 {
29807                     cls : 'tooltip-inner'
29808                 }
29809            ]
29810         };
29811         
29812         return cfg;
29813     },
29814     bind : function(el)
29815     {
29816         this.bindEl = el;
29817     },
29818     
29819     initEvents : function()
29820     {
29821         this.arrowEl = this.el.select('.arrow', true).first();
29822         this.innerEl = this.el.select('.tooltip-inner', true).first();
29823     },
29824     
29825     enter : function () {
29826        
29827         if (this.timeout != null) {
29828             clearTimeout(this.timeout);
29829         }
29830         
29831         this.hoverState = 'in';
29832          //Roo.log("enter - show");
29833         if (!this.delay || !this.delay.show) {
29834             this.show();
29835             return;
29836         }
29837         var _t = this;
29838         this.timeout = setTimeout(function () {
29839             if (_t.hoverState == 'in') {
29840                 _t.show();
29841             }
29842         }, this.delay.show);
29843     },
29844     leave : function()
29845     {
29846         clearTimeout(this.timeout);
29847     
29848         this.hoverState = 'out';
29849          if (!this.delay || !this.delay.hide) {
29850             this.hide();
29851             return;
29852         }
29853        
29854         var _t = this;
29855         this.timeout = setTimeout(function () {
29856             //Roo.log("leave - timeout");
29857             
29858             if (_t.hoverState == 'out') {
29859                 _t.hide();
29860                 Roo.bootstrap.Tooltip.currentEl = false;
29861             }
29862         }, delay);
29863     },
29864     
29865     show : function (msg)
29866     {
29867         if (!this.el) {
29868             this.render(document.body);
29869         }
29870         // set content.
29871         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29872         
29873         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29874         
29875         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29876         
29877         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29878                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29879         
29880         var placement = typeof this.placement == 'function' ?
29881             this.placement.call(this, this.el, on_el) :
29882             this.placement;
29883             
29884         var autoToken = /\s?auto?\s?/i;
29885         var autoPlace = autoToken.test(placement);
29886         if (autoPlace) {
29887             placement = placement.replace(autoToken, '') || 'top';
29888         }
29889         
29890         //this.el.detach()
29891         //this.el.setXY([0,0]);
29892         this.el.show();
29893         //this.el.dom.style.display='block';
29894         
29895         //this.el.appendTo(on_el);
29896         
29897         var p = this.getPosition();
29898         var box = this.el.getBox();
29899         
29900         if (autoPlace) {
29901             // fixme..
29902         }
29903         
29904         var align = this.alignment[placement];
29905         
29906         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29907         
29908         if(placement == 'top' || placement == 'bottom'){
29909             if(xy[0] < 0){
29910                 placement = 'right';
29911             }
29912             
29913             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29914                 placement = 'left';
29915             }
29916             
29917             var scroll = Roo.select('body', true).first().getScroll();
29918             
29919             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29920                 placement = 'top';
29921             }
29922             
29923             align = this.alignment[placement];
29924             
29925             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29926             
29927         }
29928         
29929         var elems = document.getElementsByTagName('div');
29930         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29931         for (var i = 0; i < elems.length; i++) {
29932           var zindex = Number.parseInt(
29933                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29934                 10
29935           );
29936           if (zindex > highest) {
29937             highest = zindex;
29938           }
29939         }
29940         
29941         
29942         
29943         this.el.dom.style.zIndex = highest;
29944         
29945         this.el.alignTo(this.bindEl, align[0],align[1]);
29946         //var arrow = this.el.select('.arrow',true).first();
29947         //arrow.set(align[2], 
29948         
29949         this.el.addClass(placement);
29950         this.el.addClass("bs-tooltip-"+ placement);
29951         
29952         this.el.addClass('in fade show');
29953         
29954         this.hoverState = null;
29955         
29956         if (this.el.hasClass('fade')) {
29957             // fade it?
29958         }
29959         
29960         
29961         
29962         
29963         
29964     },
29965     hide : function()
29966     {
29967          
29968         if (!this.el) {
29969             return;
29970         }
29971         //this.el.setXY([0,0]);
29972         this.el.removeClass(['show', 'in']);
29973         //this.el.hide();
29974         
29975     }
29976     
29977 });
29978  
29979
29980  /*
29981  * - LGPL
29982  *
29983  * Location Picker
29984  * 
29985  */
29986
29987 /**
29988  * @class Roo.bootstrap.LocationPicker
29989  * @extends Roo.bootstrap.Component
29990  * Bootstrap LocationPicker class
29991  * @cfg {Number} latitude Position when init default 0
29992  * @cfg {Number} longitude Position when init default 0
29993  * @cfg {Number} zoom default 15
29994  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29995  * @cfg {Boolean} mapTypeControl default false
29996  * @cfg {Boolean} disableDoubleClickZoom default false
29997  * @cfg {Boolean} scrollwheel default true
29998  * @cfg {Boolean} streetViewControl default false
29999  * @cfg {Number} radius default 0
30000  * @cfg {String} locationName
30001  * @cfg {Boolean} draggable default true
30002  * @cfg {Boolean} enableAutocomplete default false
30003  * @cfg {Boolean} enableReverseGeocode default true
30004  * @cfg {String} markerTitle
30005  * 
30006  * @constructor
30007  * Create a new LocationPicker
30008  * @param {Object} config The config object
30009  */
30010
30011
30012 Roo.bootstrap.LocationPicker = function(config){
30013     
30014     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30015     
30016     this.addEvents({
30017         /**
30018          * @event initial
30019          * Fires when the picker initialized.
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          * @param {Google Location} location
30022          */
30023         initial : true,
30024         /**
30025          * @event positionchanged
30026          * Fires when the picker position changed.
30027          * @param {Roo.bootstrap.LocationPicker} this
30028          * @param {Google Location} location
30029          */
30030         positionchanged : true,
30031         /**
30032          * @event resize
30033          * Fires when the map resize.
30034          * @param {Roo.bootstrap.LocationPicker} this
30035          */
30036         resize : true,
30037         /**
30038          * @event show
30039          * Fires when the map show.
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          */
30042         show : true,
30043         /**
30044          * @event hide
30045          * Fires when the map hide.
30046          * @param {Roo.bootstrap.LocationPicker} this
30047          */
30048         hide : true,
30049         /**
30050          * @event mapClick
30051          * Fires when click the map.
30052          * @param {Roo.bootstrap.LocationPicker} this
30053          * @param {Map event} e
30054          */
30055         mapClick : true,
30056         /**
30057          * @event mapRightClick
30058          * Fires when right click the map.
30059          * @param {Roo.bootstrap.LocationPicker} this
30060          * @param {Map event} e
30061          */
30062         mapRightClick : true,
30063         /**
30064          * @event markerClick
30065          * Fires when click the marker.
30066          * @param {Roo.bootstrap.LocationPicker} this
30067          * @param {Map event} e
30068          */
30069         markerClick : true,
30070         /**
30071          * @event markerRightClick
30072          * Fires when right click the marker.
30073          * @param {Roo.bootstrap.LocationPicker} this
30074          * @param {Map event} e
30075          */
30076         markerRightClick : true,
30077         /**
30078          * @event OverlayViewDraw
30079          * Fires when OverlayView Draw
30080          * @param {Roo.bootstrap.LocationPicker} this
30081          */
30082         OverlayViewDraw : true,
30083         /**
30084          * @event OverlayViewOnAdd
30085          * Fires when OverlayView Draw
30086          * @param {Roo.bootstrap.LocationPicker} this
30087          */
30088         OverlayViewOnAdd : true,
30089         /**
30090          * @event OverlayViewOnRemove
30091          * Fires when OverlayView Draw
30092          * @param {Roo.bootstrap.LocationPicker} this
30093          */
30094         OverlayViewOnRemove : true,
30095         /**
30096          * @event OverlayViewShow
30097          * Fires when OverlayView Draw
30098          * @param {Roo.bootstrap.LocationPicker} this
30099          * @param {Pixel} cpx
30100          */
30101         OverlayViewShow : true,
30102         /**
30103          * @event OverlayViewHide
30104          * Fires when OverlayView Draw
30105          * @param {Roo.bootstrap.LocationPicker} this
30106          */
30107         OverlayViewHide : true,
30108         /**
30109          * @event loadexception
30110          * Fires when load google lib failed.
30111          * @param {Roo.bootstrap.LocationPicker} this
30112          */
30113         loadexception : true
30114     });
30115         
30116 };
30117
30118 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30119     
30120     gMapContext: false,
30121     
30122     latitude: 0,
30123     longitude: 0,
30124     zoom: 15,
30125     mapTypeId: false,
30126     mapTypeControl: false,
30127     disableDoubleClickZoom: false,
30128     scrollwheel: true,
30129     streetViewControl: false,
30130     radius: 0,
30131     locationName: '',
30132     draggable: true,
30133     enableAutocomplete: false,
30134     enableReverseGeocode: true,
30135     markerTitle: '',
30136     
30137     getAutoCreate: function()
30138     {
30139
30140         var cfg = {
30141             tag: 'div',
30142             cls: 'roo-location-picker'
30143         };
30144         
30145         return cfg
30146     },
30147     
30148     initEvents: function(ct, position)
30149     {       
30150         if(!this.el.getWidth() || this.isApplied()){
30151             return;
30152         }
30153         
30154         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30155         
30156         this.initial();
30157     },
30158     
30159     initial: function()
30160     {
30161         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30162             this.fireEvent('loadexception', this);
30163             return;
30164         }
30165         
30166         if(!this.mapTypeId){
30167             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30168         }
30169         
30170         this.gMapContext = this.GMapContext();
30171         
30172         this.initOverlayView();
30173         
30174         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30175         
30176         var _this = this;
30177                 
30178         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30179             _this.setPosition(_this.gMapContext.marker.position);
30180         });
30181         
30182         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30183             _this.fireEvent('mapClick', this, event);
30184             
30185         });
30186
30187         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30188             _this.fireEvent('mapRightClick', this, event);
30189             
30190         });
30191         
30192         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30193             _this.fireEvent('markerClick', this, event);
30194             
30195         });
30196
30197         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30198             _this.fireEvent('markerRightClick', this, event);
30199             
30200         });
30201         
30202         this.setPosition(this.gMapContext.location);
30203         
30204         this.fireEvent('initial', this, this.gMapContext.location);
30205     },
30206     
30207     initOverlayView: function()
30208     {
30209         var _this = this;
30210         
30211         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30212             
30213             draw: function()
30214             {
30215                 _this.fireEvent('OverlayViewDraw', _this);
30216             },
30217             
30218             onAdd: function()
30219             {
30220                 _this.fireEvent('OverlayViewOnAdd', _this);
30221             },
30222             
30223             onRemove: function()
30224             {
30225                 _this.fireEvent('OverlayViewOnRemove', _this);
30226             },
30227             
30228             show: function(cpx)
30229             {
30230                 _this.fireEvent('OverlayViewShow', _this, cpx);
30231             },
30232             
30233             hide: function()
30234             {
30235                 _this.fireEvent('OverlayViewHide', _this);
30236             }
30237             
30238         });
30239     },
30240     
30241     fromLatLngToContainerPixel: function(event)
30242     {
30243         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30244     },
30245     
30246     isApplied: function() 
30247     {
30248         return this.getGmapContext() == false ? false : true;
30249     },
30250     
30251     getGmapContext: function() 
30252     {
30253         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30254     },
30255     
30256     GMapContext: function() 
30257     {
30258         var position = new google.maps.LatLng(this.latitude, this.longitude);
30259         
30260         var _map = new google.maps.Map(this.el.dom, {
30261             center: position,
30262             zoom: this.zoom,
30263             mapTypeId: this.mapTypeId,
30264             mapTypeControl: this.mapTypeControl,
30265             disableDoubleClickZoom: this.disableDoubleClickZoom,
30266             scrollwheel: this.scrollwheel,
30267             streetViewControl: this.streetViewControl,
30268             locationName: this.locationName,
30269             draggable: this.draggable,
30270             enableAutocomplete: this.enableAutocomplete,
30271             enableReverseGeocode: this.enableReverseGeocode
30272         });
30273         
30274         var _marker = new google.maps.Marker({
30275             position: position,
30276             map: _map,
30277             title: this.markerTitle,
30278             draggable: this.draggable
30279         });
30280         
30281         return {
30282             map: _map,
30283             marker: _marker,
30284             circle: null,
30285             location: position,
30286             radius: this.radius,
30287             locationName: this.locationName,
30288             addressComponents: {
30289                 formatted_address: null,
30290                 addressLine1: null,
30291                 addressLine2: null,
30292                 streetName: null,
30293                 streetNumber: null,
30294                 city: null,
30295                 district: null,
30296                 state: null,
30297                 stateOrProvince: null
30298             },
30299             settings: this,
30300             domContainer: this.el.dom,
30301             geodecoder: new google.maps.Geocoder()
30302         };
30303     },
30304     
30305     drawCircle: function(center, radius, options) 
30306     {
30307         if (this.gMapContext.circle != null) {
30308             this.gMapContext.circle.setMap(null);
30309         }
30310         if (radius > 0) {
30311             radius *= 1;
30312             options = Roo.apply({}, options, {
30313                 strokeColor: "#0000FF",
30314                 strokeOpacity: .35,
30315                 strokeWeight: 2,
30316                 fillColor: "#0000FF",
30317                 fillOpacity: .2
30318             });
30319             
30320             options.map = this.gMapContext.map;
30321             options.radius = radius;
30322             options.center = center;
30323             this.gMapContext.circle = new google.maps.Circle(options);
30324             return this.gMapContext.circle;
30325         }
30326         
30327         return null;
30328     },
30329     
30330     setPosition: function(location) 
30331     {
30332         this.gMapContext.location = location;
30333         this.gMapContext.marker.setPosition(location);
30334         this.gMapContext.map.panTo(location);
30335         this.drawCircle(location, this.gMapContext.radius, {});
30336         
30337         var _this = this;
30338         
30339         if (this.gMapContext.settings.enableReverseGeocode) {
30340             this.gMapContext.geodecoder.geocode({
30341                 latLng: this.gMapContext.location
30342             }, function(results, status) {
30343                 
30344                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30345                     _this.gMapContext.locationName = results[0].formatted_address;
30346                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30347                     
30348                     _this.fireEvent('positionchanged', this, location);
30349                 }
30350             });
30351             
30352             return;
30353         }
30354         
30355         this.fireEvent('positionchanged', this, location);
30356     },
30357     
30358     resize: function()
30359     {
30360         google.maps.event.trigger(this.gMapContext.map, "resize");
30361         
30362         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30363         
30364         this.fireEvent('resize', this);
30365     },
30366     
30367     setPositionByLatLng: function(latitude, longitude)
30368     {
30369         this.setPosition(new google.maps.LatLng(latitude, longitude));
30370     },
30371     
30372     getCurrentPosition: function() 
30373     {
30374         return {
30375             latitude: this.gMapContext.location.lat(),
30376             longitude: this.gMapContext.location.lng()
30377         };
30378     },
30379     
30380     getAddressName: function() 
30381     {
30382         return this.gMapContext.locationName;
30383     },
30384     
30385     getAddressComponents: function() 
30386     {
30387         return this.gMapContext.addressComponents;
30388     },
30389     
30390     address_component_from_google_geocode: function(address_components) 
30391     {
30392         var result = {};
30393         
30394         for (var i = 0; i < address_components.length; i++) {
30395             var component = address_components[i];
30396             if (component.types.indexOf("postal_code") >= 0) {
30397                 result.postalCode = component.short_name;
30398             } else if (component.types.indexOf("street_number") >= 0) {
30399                 result.streetNumber = component.short_name;
30400             } else if (component.types.indexOf("route") >= 0) {
30401                 result.streetName = component.short_name;
30402             } else if (component.types.indexOf("neighborhood") >= 0) {
30403                 result.city = component.short_name;
30404             } else if (component.types.indexOf("locality") >= 0) {
30405                 result.city = component.short_name;
30406             } else if (component.types.indexOf("sublocality") >= 0) {
30407                 result.district = component.short_name;
30408             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30409                 result.stateOrProvince = component.short_name;
30410             } else if (component.types.indexOf("country") >= 0) {
30411                 result.country = component.short_name;
30412             }
30413         }
30414         
30415         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30416         result.addressLine2 = "";
30417         return result;
30418     },
30419     
30420     setZoomLevel: function(zoom)
30421     {
30422         this.gMapContext.map.setZoom(zoom);
30423     },
30424     
30425     show: function()
30426     {
30427         if(!this.el){
30428             return;
30429         }
30430         
30431         this.el.show();
30432         
30433         this.resize();
30434         
30435         this.fireEvent('show', this);
30436     },
30437     
30438     hide: function()
30439     {
30440         if(!this.el){
30441             return;
30442         }
30443         
30444         this.el.hide();
30445         
30446         this.fireEvent('hide', this);
30447     }
30448     
30449 });
30450
30451 Roo.apply(Roo.bootstrap.LocationPicker, {
30452     
30453     OverlayView : function(map, options)
30454     {
30455         options = options || {};
30456         
30457         this.setMap(map);
30458     }
30459     
30460     
30461 });/**
30462  * @class Roo.bootstrap.Alert
30463  * @extends Roo.bootstrap.Component
30464  * Bootstrap Alert class - shows an alert area box
30465  * eg
30466  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30467   Enter a valid email address
30468 </div>
30469  * @licence LGPL
30470  * @cfg {String} title The title of alert
30471  * @cfg {String} html The content of alert
30472  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30473  * @cfg {String} fa font-awesomeicon
30474  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30475  * @cfg {Boolean} close true to show a x closer
30476  * 
30477  * 
30478  * @constructor
30479  * Create a new alert
30480  * @param {Object} config The config object
30481  */
30482
30483
30484 Roo.bootstrap.Alert = function(config){
30485     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30486     
30487 };
30488
30489 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30490     
30491     title: '',
30492     html: '',
30493     weight: false,
30494     fa: false,
30495     faicon: false, // BC
30496     close : false,
30497     
30498     
30499     getAutoCreate : function()
30500     {
30501         
30502         var cfg = {
30503             tag : 'div',
30504             cls : 'alert',
30505             cn : [
30506                 {
30507                     tag: 'button',
30508                     type :  "button",
30509                     cls: "close",
30510                     html : '×',
30511                     style : this.close ? '' : 'display:none'
30512                 },
30513                 {
30514                     tag : 'i',
30515                     cls : 'roo-alert-icon'
30516                     
30517                 },
30518                 {
30519                     tag : 'b',
30520                     cls : 'roo-alert-title',
30521                     html : this.title
30522                 },
30523                 {
30524                     tag : 'span',
30525                     cls : 'roo-alert-text',
30526                     html : this.html
30527                 }
30528             ]
30529         };
30530         
30531         if(this.faicon){
30532             cfg.cn[0].cls += ' fa ' + this.faicon;
30533         }
30534         if(this.fa){
30535             cfg.cn[0].cls += ' fa ' + this.fa;
30536         }
30537         
30538         if(this.weight){
30539             cfg.cls += ' alert-' + this.weight;
30540         }
30541         
30542         return cfg;
30543     },
30544     
30545     initEvents: function() 
30546     {
30547         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30548         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30549         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30550         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30551         if (this.seconds > 0) {
30552             this.hide.defer(this.seconds, this);
30553         }
30554     },
30555     /**
30556      * Set the Title Message HTML
30557      * @param {String} html
30558      */
30559     setTitle : function(str)
30560     {
30561         this.titleEl.dom.innerHTML = str;
30562     },
30563      
30564      /**
30565      * Set the Body Message HTML
30566      * @param {String} html
30567      */
30568     setHtml : function(str)
30569     {
30570         this.htmlEl.dom.innerHTML = str;
30571     },
30572     /**
30573      * Set the Weight of the alert
30574      * @param {String} (success|info|warning|danger) weight
30575      */
30576     
30577     setWeight : function(weight)
30578     {
30579         if(this.weight){
30580             this.el.removeClass('alert-' + this.weight);
30581         }
30582         
30583         this.weight = weight;
30584         
30585         this.el.addClass('alert-' + this.weight);
30586     },
30587       /**
30588      * Set the Icon of the alert
30589      * @param {String} see fontawsome names (name without the 'fa-' bit)
30590      */
30591     setIcon : function(icon)
30592     {
30593         if(this.faicon){
30594             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30595         }
30596         
30597         this.faicon = icon;
30598         
30599         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30600     },
30601     /**
30602      * Hide the Alert
30603      */
30604     hide: function() 
30605     {
30606         this.el.hide();   
30607     },
30608     /**
30609      * Show the Alert
30610      */
30611     show: function() 
30612     {  
30613         this.el.show();   
30614     }
30615     
30616 });
30617
30618  
30619 /*
30620 * Licence: LGPL
30621 */
30622
30623 /**
30624  * @class Roo.bootstrap.UploadCropbox
30625  * @extends Roo.bootstrap.Component
30626  * Bootstrap UploadCropbox class
30627  * @cfg {String} emptyText show when image has been loaded
30628  * @cfg {String} rotateNotify show when image too small to rotate
30629  * @cfg {Number} errorTimeout default 3000
30630  * @cfg {Number} minWidth default 300
30631  * @cfg {Number} minHeight default 300
30632  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30633  * @cfg {Boolean} isDocument (true|false) default false
30634  * @cfg {String} url action url
30635  * @cfg {String} paramName default 'imageUpload'
30636  * @cfg {String} method default POST
30637  * @cfg {Boolean} loadMask (true|false) default true
30638  * @cfg {Boolean} loadingText default 'Loading...'
30639  * 
30640  * @constructor
30641  * Create a new UploadCropbox
30642  * @param {Object} config The config object
30643  */
30644
30645 Roo.bootstrap.UploadCropbox = function(config){
30646     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30647     
30648     this.addEvents({
30649         /**
30650          * @event beforeselectfile
30651          * Fire before select file
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          */
30654         "beforeselectfile" : true,
30655         /**
30656          * @event initial
30657          * Fire after initEvent
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          */
30660         "initial" : true,
30661         /**
30662          * @event crop
30663          * Fire after initEvent
30664          * @param {Roo.bootstrap.UploadCropbox} this
30665          * @param {String} data
30666          */
30667         "crop" : true,
30668         /**
30669          * @event prepare
30670          * Fire when preparing the file data
30671          * @param {Roo.bootstrap.UploadCropbox} this
30672          * @param {Object} file
30673          */
30674         "prepare" : true,
30675         /**
30676          * @event exception
30677          * Fire when get exception
30678          * @param {Roo.bootstrap.UploadCropbox} this
30679          * @param {XMLHttpRequest} xhr
30680          */
30681         "exception" : true,
30682         /**
30683          * @event beforeloadcanvas
30684          * Fire before load the canvas
30685          * @param {Roo.bootstrap.UploadCropbox} this
30686          * @param {String} src
30687          */
30688         "beforeloadcanvas" : true,
30689         /**
30690          * @event trash
30691          * Fire when trash image
30692          * @param {Roo.bootstrap.UploadCropbox} this
30693          */
30694         "trash" : true,
30695         /**
30696          * @event download
30697          * Fire when download the image
30698          * @param {Roo.bootstrap.UploadCropbox} this
30699          */
30700         "download" : true,
30701         /**
30702          * @event footerbuttonclick
30703          * Fire when footerbuttonclick
30704          * @param {Roo.bootstrap.UploadCropbox} this
30705          * @param {String} type
30706          */
30707         "footerbuttonclick" : true,
30708         /**
30709          * @event resize
30710          * Fire when resize
30711          * @param {Roo.bootstrap.UploadCropbox} this
30712          */
30713         "resize" : true,
30714         /**
30715          * @event rotate
30716          * Fire when rotate the image
30717          * @param {Roo.bootstrap.UploadCropbox} this
30718          * @param {String} pos
30719          */
30720         "rotate" : true,
30721         /**
30722          * @event inspect
30723          * Fire when inspect the file
30724          * @param {Roo.bootstrap.UploadCropbox} this
30725          * @param {Object} file
30726          */
30727         "inspect" : true,
30728         /**
30729          * @event upload
30730          * Fire when xhr upload the file
30731          * @param {Roo.bootstrap.UploadCropbox} this
30732          * @param {Object} data
30733          */
30734         "upload" : true,
30735         /**
30736          * @event arrange
30737          * Fire when arrange the file data
30738          * @param {Roo.bootstrap.UploadCropbox} this
30739          * @param {Object} formData
30740          */
30741         "arrange" : true
30742     });
30743     
30744     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30745 };
30746
30747 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30748     
30749     emptyText : 'Click to upload image',
30750     rotateNotify : 'Image is too small to rotate',
30751     errorTimeout : 3000,
30752     scale : 0,
30753     baseScale : 1,
30754     rotate : 0,
30755     dragable : false,
30756     pinching : false,
30757     mouseX : 0,
30758     mouseY : 0,
30759     cropData : false,
30760     minWidth : 300,
30761     minHeight : 300,
30762     file : false,
30763     exif : {},
30764     baseRotate : 1,
30765     cropType : 'image/jpeg',
30766     buttons : false,
30767     canvasLoaded : false,
30768     isDocument : false,
30769     method : 'POST',
30770     paramName : 'imageUpload',
30771     loadMask : true,
30772     loadingText : 'Loading...',
30773     maskEl : false,
30774     
30775     getAutoCreate : function()
30776     {
30777         var cfg = {
30778             tag : 'div',
30779             cls : 'roo-upload-cropbox',
30780             cn : [
30781                 {
30782                     tag : 'input',
30783                     cls : 'roo-upload-cropbox-selector',
30784                     type : 'file'
30785                 },
30786                 {
30787                     tag : 'div',
30788                     cls : 'roo-upload-cropbox-body',
30789                     style : 'cursor:pointer',
30790                     cn : [
30791                         {
30792                             tag : 'div',
30793                             cls : 'roo-upload-cropbox-preview'
30794                         },
30795                         {
30796                             tag : 'div',
30797                             cls : 'roo-upload-cropbox-thumb'
30798                         },
30799                         {
30800                             tag : 'div',
30801                             cls : 'roo-upload-cropbox-empty-notify',
30802                             html : this.emptyText
30803                         },
30804                         {
30805                             tag : 'div',
30806                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30807                             html : this.rotateNotify
30808                         }
30809                     ]
30810                 },
30811                 {
30812                     tag : 'div',
30813                     cls : 'roo-upload-cropbox-footer',
30814                     cn : {
30815                         tag : 'div',
30816                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30817                         cn : []
30818                     }
30819                 }
30820             ]
30821         };
30822         
30823         return cfg;
30824     },
30825     
30826     onRender : function(ct, position)
30827     {
30828         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30829         
30830         if (this.buttons.length) {
30831             
30832             Roo.each(this.buttons, function(bb) {
30833                 
30834                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30835                 
30836                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30837                 
30838             }, this);
30839         }
30840         
30841         if(this.loadMask){
30842             this.maskEl = this.el;
30843         }
30844     },
30845     
30846     initEvents : function()
30847     {
30848         this.urlAPI = (window.createObjectURL && window) || 
30849                                 (window.URL && URL.revokeObjectURL && URL) || 
30850                                 (window.webkitURL && webkitURL);
30851                         
30852         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30853         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30854         
30855         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30856         this.selectorEl.hide();
30857         
30858         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30859         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30860         
30861         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30862         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30863         this.thumbEl.hide();
30864         
30865         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30866         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30867         
30868         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30869         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30870         this.errorEl.hide();
30871         
30872         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30873         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30874         this.footerEl.hide();
30875         
30876         this.setThumbBoxSize();
30877         
30878         this.bind();
30879         
30880         this.resize();
30881         
30882         this.fireEvent('initial', this);
30883     },
30884
30885     bind : function()
30886     {
30887         var _this = this;
30888         
30889         window.addEventListener("resize", function() { _this.resize(); } );
30890         
30891         this.bodyEl.on('click', this.beforeSelectFile, this);
30892         
30893         if(Roo.isTouch){
30894             this.bodyEl.on('touchstart', this.onTouchStart, this);
30895             this.bodyEl.on('touchmove', this.onTouchMove, this);
30896             this.bodyEl.on('touchend', this.onTouchEnd, this);
30897         }
30898         
30899         if(!Roo.isTouch){
30900             this.bodyEl.on('mousedown', this.onMouseDown, this);
30901             this.bodyEl.on('mousemove', this.onMouseMove, this);
30902             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30903             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30904             Roo.get(document).on('mouseup', this.onMouseUp, this);
30905         }
30906         
30907         this.selectorEl.on('change', this.onFileSelected, this);
30908     },
30909     
30910     reset : function()
30911     {    
30912         this.scale = 0;
30913         this.baseScale = 1;
30914         this.rotate = 0;
30915         this.baseRotate = 1;
30916         this.dragable = false;
30917         this.pinching = false;
30918         this.mouseX = 0;
30919         this.mouseY = 0;
30920         this.cropData = false;
30921         this.notifyEl.dom.innerHTML = this.emptyText;
30922         
30923         this.selectorEl.dom.value = '';
30924         
30925     },
30926     
30927     resize : function()
30928     {
30929         if(this.fireEvent('resize', this) != false){
30930             this.setThumbBoxPosition();
30931             this.setCanvasPosition();
30932         }
30933     },
30934     
30935     onFooterButtonClick : function(e, el, o, type)
30936     {
30937         switch (type) {
30938             case 'rotate-left' :
30939                 this.onRotateLeft(e);
30940                 break;
30941             case 'rotate-right' :
30942                 this.onRotateRight(e);
30943                 break;
30944             case 'picture' :
30945                 this.beforeSelectFile(e);
30946                 break;
30947             case 'trash' :
30948                 this.trash(e);
30949                 break;
30950             case 'crop' :
30951                 this.crop(e);
30952                 break;
30953             case 'download' :
30954                 this.download(e);
30955                 break;
30956             default :
30957                 break;
30958         }
30959         
30960         this.fireEvent('footerbuttonclick', this, type);
30961     },
30962     
30963     beforeSelectFile : function(e)
30964     {
30965         e.preventDefault();
30966         
30967         if(this.fireEvent('beforeselectfile', this) != false){
30968             this.selectorEl.dom.click();
30969         }
30970     },
30971     
30972     onFileSelected : function(e)
30973     {
30974         e.preventDefault();
30975         
30976         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30977             return;
30978         }
30979         
30980         var file = this.selectorEl.dom.files[0];
30981         
30982         if(this.fireEvent('inspect', this, file) != false){
30983             this.prepare(file);
30984         }
30985         
30986     },
30987     
30988     trash : function(e)
30989     {
30990         this.fireEvent('trash', this);
30991     },
30992     
30993     download : function(e)
30994     {
30995         this.fireEvent('download', this);
30996     },
30997     
30998     loadCanvas : function(src)
30999     {   
31000         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31001             
31002             this.reset();
31003             
31004             this.imageEl = document.createElement('img');
31005             
31006             var _this = this;
31007             
31008             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31009             
31010             this.imageEl.src = src;
31011         }
31012     },
31013     
31014     onLoadCanvas : function()
31015     {   
31016         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31017         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31018         
31019         this.bodyEl.un('click', this.beforeSelectFile, this);
31020         
31021         this.notifyEl.hide();
31022         this.thumbEl.show();
31023         this.footerEl.show();
31024         
31025         this.baseRotateLevel();
31026         
31027         if(this.isDocument){
31028             this.setThumbBoxSize();
31029         }
31030         
31031         this.setThumbBoxPosition();
31032         
31033         this.baseScaleLevel();
31034         
31035         this.draw();
31036         
31037         this.resize();
31038         
31039         this.canvasLoaded = true;
31040         
31041         if(this.loadMask){
31042             this.maskEl.unmask();
31043         }
31044         
31045     },
31046     
31047     setCanvasPosition : function()
31048     {   
31049         if(!this.canvasEl){
31050             return;
31051         }
31052         
31053         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31054         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31055         
31056         this.previewEl.setLeft(pw);
31057         this.previewEl.setTop(ph);
31058         
31059     },
31060     
31061     onMouseDown : function(e)
31062     {   
31063         e.stopEvent();
31064         
31065         this.dragable = true;
31066         this.pinching = false;
31067         
31068         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31069             this.dragable = false;
31070             return;
31071         }
31072         
31073         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31074         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31075         
31076     },
31077     
31078     onMouseMove : function(e)
31079     {   
31080         e.stopEvent();
31081         
31082         if(!this.canvasLoaded){
31083             return;
31084         }
31085         
31086         if (!this.dragable){
31087             return;
31088         }
31089         
31090         var minX = Math.ceil(this.thumbEl.getLeft(true));
31091         var minY = Math.ceil(this.thumbEl.getTop(true));
31092         
31093         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31094         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31095         
31096         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31097         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31098         
31099         x = x - this.mouseX;
31100         y = y - this.mouseY;
31101         
31102         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31103         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31104         
31105         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31106         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31107         
31108         this.previewEl.setLeft(bgX);
31109         this.previewEl.setTop(bgY);
31110         
31111         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31112         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31113     },
31114     
31115     onMouseUp : function(e)
31116     {   
31117         e.stopEvent();
31118         
31119         this.dragable = false;
31120     },
31121     
31122     onMouseWheel : function(e)
31123     {   
31124         e.stopEvent();
31125         
31126         this.startScale = this.scale;
31127         
31128         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31129         
31130         if(!this.zoomable()){
31131             this.scale = this.startScale;
31132             return;
31133         }
31134         
31135         this.draw();
31136         
31137         return;
31138     },
31139     
31140     zoomable : function()
31141     {
31142         var minScale = this.thumbEl.getWidth() / this.minWidth;
31143         
31144         if(this.minWidth < this.minHeight){
31145             minScale = this.thumbEl.getHeight() / this.minHeight;
31146         }
31147         
31148         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31149         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31150         
31151         if(
31152                 this.isDocument &&
31153                 (this.rotate == 0 || this.rotate == 180) && 
31154                 (
31155                     width > this.imageEl.OriginWidth || 
31156                     height > this.imageEl.OriginHeight ||
31157                     (width < this.minWidth && height < this.minHeight)
31158                 )
31159         ){
31160             return false;
31161         }
31162         
31163         if(
31164                 this.isDocument &&
31165                 (this.rotate == 90 || this.rotate == 270) && 
31166                 (
31167                     width > this.imageEl.OriginWidth || 
31168                     height > this.imageEl.OriginHeight ||
31169                     (width < this.minHeight && height < this.minWidth)
31170                 )
31171         ){
31172             return false;
31173         }
31174         
31175         if(
31176                 !this.isDocument &&
31177                 (this.rotate == 0 || this.rotate == 180) && 
31178                 (
31179                     width < this.minWidth || 
31180                     width > this.imageEl.OriginWidth || 
31181                     height < this.minHeight || 
31182                     height > this.imageEl.OriginHeight
31183                 )
31184         ){
31185             return false;
31186         }
31187         
31188         if(
31189                 !this.isDocument &&
31190                 (this.rotate == 90 || this.rotate == 270) && 
31191                 (
31192                     width < this.minHeight || 
31193                     width > this.imageEl.OriginWidth || 
31194                     height < this.minWidth || 
31195                     height > this.imageEl.OriginHeight
31196                 )
31197         ){
31198             return false;
31199         }
31200         
31201         return true;
31202         
31203     },
31204     
31205     onRotateLeft : function(e)
31206     {   
31207         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31208             
31209             var minScale = this.thumbEl.getWidth() / this.minWidth;
31210             
31211             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31212             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31213             
31214             this.startScale = this.scale;
31215             
31216             while (this.getScaleLevel() < minScale){
31217             
31218                 this.scale = this.scale + 1;
31219                 
31220                 if(!this.zoomable()){
31221                     break;
31222                 }
31223                 
31224                 if(
31225                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31226                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31227                 ){
31228                     continue;
31229                 }
31230                 
31231                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31232
31233                 this.draw();
31234                 
31235                 return;
31236             }
31237             
31238             this.scale = this.startScale;
31239             
31240             this.onRotateFail();
31241             
31242             return false;
31243         }
31244         
31245         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31246
31247         if(this.isDocument){
31248             this.setThumbBoxSize();
31249             this.setThumbBoxPosition();
31250             this.setCanvasPosition();
31251         }
31252         
31253         this.draw();
31254         
31255         this.fireEvent('rotate', this, 'left');
31256         
31257     },
31258     
31259     onRotateRight : function(e)
31260     {
31261         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31262             
31263             var minScale = this.thumbEl.getWidth() / this.minWidth;
31264         
31265             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31266             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31267             
31268             this.startScale = this.scale;
31269             
31270             while (this.getScaleLevel() < minScale){
31271             
31272                 this.scale = this.scale + 1;
31273                 
31274                 if(!this.zoomable()){
31275                     break;
31276                 }
31277                 
31278                 if(
31279                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31280                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31281                 ){
31282                     continue;
31283                 }
31284                 
31285                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31286
31287                 this.draw();
31288                 
31289                 return;
31290             }
31291             
31292             this.scale = this.startScale;
31293             
31294             this.onRotateFail();
31295             
31296             return false;
31297         }
31298         
31299         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31300
31301         if(this.isDocument){
31302             this.setThumbBoxSize();
31303             this.setThumbBoxPosition();
31304             this.setCanvasPosition();
31305         }
31306         
31307         this.draw();
31308         
31309         this.fireEvent('rotate', this, 'right');
31310     },
31311     
31312     onRotateFail : function()
31313     {
31314         this.errorEl.show(true);
31315         
31316         var _this = this;
31317         
31318         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31319     },
31320     
31321     draw : function()
31322     {
31323         this.previewEl.dom.innerHTML = '';
31324         
31325         var canvasEl = document.createElement("canvas");
31326         
31327         var contextEl = canvasEl.getContext("2d");
31328         
31329         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31330         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31331         var center = this.imageEl.OriginWidth / 2;
31332         
31333         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31334             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31335             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31336             center = this.imageEl.OriginHeight / 2;
31337         }
31338         
31339         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31340         
31341         contextEl.translate(center, center);
31342         contextEl.rotate(this.rotate * Math.PI / 180);
31343
31344         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31345         
31346         this.canvasEl = document.createElement("canvas");
31347         
31348         this.contextEl = this.canvasEl.getContext("2d");
31349         
31350         switch (this.rotate) {
31351             case 0 :
31352                 
31353                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31354                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31355                 
31356                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31357                 
31358                 break;
31359             case 90 : 
31360                 
31361                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31362                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31363                 
31364                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31365                     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);
31366                     break;
31367                 }
31368                 
31369                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31370                 
31371                 break;
31372             case 180 :
31373                 
31374                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31375                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31376                 
31377                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31378                     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);
31379                     break;
31380                 }
31381                 
31382                 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);
31383                 
31384                 break;
31385             case 270 :
31386                 
31387                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31388                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31389         
31390                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31391                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31392                     break;
31393                 }
31394                 
31395                 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);
31396                 
31397                 break;
31398             default : 
31399                 break;
31400         }
31401         
31402         this.previewEl.appendChild(this.canvasEl);
31403         
31404         this.setCanvasPosition();
31405     },
31406     
31407     crop : function()
31408     {
31409         if(!this.canvasLoaded){
31410             return;
31411         }
31412         
31413         var imageCanvas = document.createElement("canvas");
31414         
31415         var imageContext = imageCanvas.getContext("2d");
31416         
31417         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31418         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31419         
31420         var center = imageCanvas.width / 2;
31421         
31422         imageContext.translate(center, center);
31423         
31424         imageContext.rotate(this.rotate * Math.PI / 180);
31425         
31426         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31427         
31428         var canvas = document.createElement("canvas");
31429         
31430         var context = canvas.getContext("2d");
31431                 
31432         canvas.width = this.minWidth;
31433         canvas.height = this.minHeight;
31434
31435         switch (this.rotate) {
31436             case 0 :
31437                 
31438                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31439                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31440                 
31441                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31442                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31443                 
31444                 var targetWidth = this.minWidth - 2 * x;
31445                 var targetHeight = this.minHeight - 2 * y;
31446                 
31447                 var scale = 1;
31448                 
31449                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31450                     scale = targetWidth / width;
31451                 }
31452                 
31453                 if(x > 0 && y == 0){
31454                     scale = targetHeight / height;
31455                 }
31456                 
31457                 if(x > 0 && y > 0){
31458                     scale = targetWidth / width;
31459                     
31460                     if(width < height){
31461                         scale = targetHeight / height;
31462                     }
31463                 }
31464                 
31465                 context.scale(scale, scale);
31466                 
31467                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31468                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31469
31470                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31471                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31472
31473                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31474                 
31475                 break;
31476             case 90 : 
31477                 
31478                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31479                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31480                 
31481                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31482                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31483                 
31484                 var targetWidth = this.minWidth - 2 * x;
31485                 var targetHeight = this.minHeight - 2 * y;
31486                 
31487                 var scale = 1;
31488                 
31489                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31490                     scale = targetWidth / width;
31491                 }
31492                 
31493                 if(x > 0 && y == 0){
31494                     scale = targetHeight / height;
31495                 }
31496                 
31497                 if(x > 0 && y > 0){
31498                     scale = targetWidth / width;
31499                     
31500                     if(width < height){
31501                         scale = targetHeight / height;
31502                     }
31503                 }
31504                 
31505                 context.scale(scale, scale);
31506                 
31507                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31508                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31509
31510                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31511                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31512                 
31513                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31514                 
31515                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31516                 
31517                 break;
31518             case 180 :
31519                 
31520                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31521                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31522                 
31523                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31524                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31525                 
31526                 var targetWidth = this.minWidth - 2 * x;
31527                 var targetHeight = this.minHeight - 2 * y;
31528                 
31529                 var scale = 1;
31530                 
31531                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31532                     scale = targetWidth / width;
31533                 }
31534                 
31535                 if(x > 0 && y == 0){
31536                     scale = targetHeight / height;
31537                 }
31538                 
31539                 if(x > 0 && y > 0){
31540                     scale = targetWidth / width;
31541                     
31542                     if(width < height){
31543                         scale = targetHeight / height;
31544                     }
31545                 }
31546                 
31547                 context.scale(scale, scale);
31548                 
31549                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31550                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31551
31552                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31553                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31554
31555                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31556                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31557                 
31558                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31559                 
31560                 break;
31561             case 270 :
31562                 
31563                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31564                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31565                 
31566                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31567                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31568                 
31569                 var targetWidth = this.minWidth - 2 * x;
31570                 var targetHeight = this.minHeight - 2 * y;
31571                 
31572                 var scale = 1;
31573                 
31574                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31575                     scale = targetWidth / width;
31576                 }
31577                 
31578                 if(x > 0 && y == 0){
31579                     scale = targetHeight / height;
31580                 }
31581                 
31582                 if(x > 0 && y > 0){
31583                     scale = targetWidth / width;
31584                     
31585                     if(width < height){
31586                         scale = targetHeight / height;
31587                     }
31588                 }
31589                 
31590                 context.scale(scale, scale);
31591                 
31592                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31593                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31594
31595                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31596                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31597                 
31598                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31599                 
31600                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31601                 
31602                 break;
31603             default : 
31604                 break;
31605         }
31606         
31607         this.cropData = canvas.toDataURL(this.cropType);
31608         
31609         if(this.fireEvent('crop', this, this.cropData) !== false){
31610             this.process(this.file, this.cropData);
31611         }
31612         
31613         return;
31614         
31615     },
31616     
31617     setThumbBoxSize : function()
31618     {
31619         var width, height;
31620         
31621         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31622             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31623             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31624             
31625             this.minWidth = width;
31626             this.minHeight = height;
31627             
31628             if(this.rotate == 90 || this.rotate == 270){
31629                 this.minWidth = height;
31630                 this.minHeight = width;
31631             }
31632         }
31633         
31634         height = 300;
31635         width = Math.ceil(this.minWidth * height / this.minHeight);
31636         
31637         if(this.minWidth > this.minHeight){
31638             width = 300;
31639             height = Math.ceil(this.minHeight * width / this.minWidth);
31640         }
31641         
31642         this.thumbEl.setStyle({
31643             width : width + 'px',
31644             height : height + 'px'
31645         });
31646
31647         return;
31648             
31649     },
31650     
31651     setThumbBoxPosition : function()
31652     {
31653         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31654         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31655         
31656         this.thumbEl.setLeft(x);
31657         this.thumbEl.setTop(y);
31658         
31659     },
31660     
31661     baseRotateLevel : function()
31662     {
31663         this.baseRotate = 1;
31664         
31665         if(
31666                 typeof(this.exif) != 'undefined' &&
31667                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31668                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31669         ){
31670             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31671         }
31672         
31673         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31674         
31675     },
31676     
31677     baseScaleLevel : function()
31678     {
31679         var width, height;
31680         
31681         if(this.isDocument){
31682             
31683             if(this.baseRotate == 6 || this.baseRotate == 8){
31684             
31685                 height = this.thumbEl.getHeight();
31686                 this.baseScale = height / this.imageEl.OriginWidth;
31687
31688                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31689                     width = this.thumbEl.getWidth();
31690                     this.baseScale = width / this.imageEl.OriginHeight;
31691                 }
31692
31693                 return;
31694             }
31695
31696             height = this.thumbEl.getHeight();
31697             this.baseScale = height / this.imageEl.OriginHeight;
31698
31699             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31700                 width = this.thumbEl.getWidth();
31701                 this.baseScale = width / this.imageEl.OriginWidth;
31702             }
31703
31704             return;
31705         }
31706         
31707         if(this.baseRotate == 6 || this.baseRotate == 8){
31708             
31709             width = this.thumbEl.getHeight();
31710             this.baseScale = width / this.imageEl.OriginHeight;
31711             
31712             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31713                 height = this.thumbEl.getWidth();
31714                 this.baseScale = height / this.imageEl.OriginHeight;
31715             }
31716             
31717             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31718                 height = this.thumbEl.getWidth();
31719                 this.baseScale = height / this.imageEl.OriginHeight;
31720                 
31721                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31722                     width = this.thumbEl.getHeight();
31723                     this.baseScale = width / this.imageEl.OriginWidth;
31724                 }
31725             }
31726             
31727             return;
31728         }
31729         
31730         width = this.thumbEl.getWidth();
31731         this.baseScale = width / this.imageEl.OriginWidth;
31732         
31733         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31734             height = this.thumbEl.getHeight();
31735             this.baseScale = height / this.imageEl.OriginHeight;
31736         }
31737         
31738         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31739             
31740             height = this.thumbEl.getHeight();
31741             this.baseScale = height / this.imageEl.OriginHeight;
31742             
31743             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31744                 width = this.thumbEl.getWidth();
31745                 this.baseScale = width / this.imageEl.OriginWidth;
31746             }
31747             
31748         }
31749         
31750         return;
31751     },
31752     
31753     getScaleLevel : function()
31754     {
31755         return this.baseScale * Math.pow(1.1, this.scale);
31756     },
31757     
31758     onTouchStart : function(e)
31759     {
31760         if(!this.canvasLoaded){
31761             this.beforeSelectFile(e);
31762             return;
31763         }
31764         
31765         var touches = e.browserEvent.touches;
31766         
31767         if(!touches){
31768             return;
31769         }
31770         
31771         if(touches.length == 1){
31772             this.onMouseDown(e);
31773             return;
31774         }
31775         
31776         if(touches.length != 2){
31777             return;
31778         }
31779         
31780         var coords = [];
31781         
31782         for(var i = 0, finger; finger = touches[i]; i++){
31783             coords.push(finger.pageX, finger.pageY);
31784         }
31785         
31786         var x = Math.pow(coords[0] - coords[2], 2);
31787         var y = Math.pow(coords[1] - coords[3], 2);
31788         
31789         this.startDistance = Math.sqrt(x + y);
31790         
31791         this.startScale = this.scale;
31792         
31793         this.pinching = true;
31794         this.dragable = false;
31795         
31796     },
31797     
31798     onTouchMove : function(e)
31799     {
31800         if(!this.pinching && !this.dragable){
31801             return;
31802         }
31803         
31804         var touches = e.browserEvent.touches;
31805         
31806         if(!touches){
31807             return;
31808         }
31809         
31810         if(this.dragable){
31811             this.onMouseMove(e);
31812             return;
31813         }
31814         
31815         var coords = [];
31816         
31817         for(var i = 0, finger; finger = touches[i]; i++){
31818             coords.push(finger.pageX, finger.pageY);
31819         }
31820         
31821         var x = Math.pow(coords[0] - coords[2], 2);
31822         var y = Math.pow(coords[1] - coords[3], 2);
31823         
31824         this.endDistance = Math.sqrt(x + y);
31825         
31826         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31827         
31828         if(!this.zoomable()){
31829             this.scale = this.startScale;
31830             return;
31831         }
31832         
31833         this.draw();
31834         
31835     },
31836     
31837     onTouchEnd : function(e)
31838     {
31839         this.pinching = false;
31840         this.dragable = false;
31841         
31842     },
31843     
31844     process : function(file, crop)
31845     {
31846         if(this.loadMask){
31847             this.maskEl.mask(this.loadingText);
31848         }
31849         
31850         this.xhr = new XMLHttpRequest();
31851         
31852         file.xhr = this.xhr;
31853
31854         this.xhr.open(this.method, this.url, true);
31855         
31856         var headers = {
31857             "Accept": "application/json",
31858             "Cache-Control": "no-cache",
31859             "X-Requested-With": "XMLHttpRequest"
31860         };
31861         
31862         for (var headerName in headers) {
31863             var headerValue = headers[headerName];
31864             if (headerValue) {
31865                 this.xhr.setRequestHeader(headerName, headerValue);
31866             }
31867         }
31868         
31869         var _this = this;
31870         
31871         this.xhr.onload = function()
31872         {
31873             _this.xhrOnLoad(_this.xhr);
31874         }
31875         
31876         this.xhr.onerror = function()
31877         {
31878             _this.xhrOnError(_this.xhr);
31879         }
31880         
31881         var formData = new FormData();
31882
31883         formData.append('returnHTML', 'NO');
31884         
31885         if(crop){
31886             formData.append('crop', crop);
31887         }
31888         
31889         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31890             formData.append(this.paramName, file, file.name);
31891         }
31892         
31893         if(typeof(file.filename) != 'undefined'){
31894             formData.append('filename', file.filename);
31895         }
31896         
31897         if(typeof(file.mimetype) != 'undefined'){
31898             formData.append('mimetype', file.mimetype);
31899         }
31900         
31901         if(this.fireEvent('arrange', this, formData) != false){
31902             this.xhr.send(formData);
31903         };
31904     },
31905     
31906     xhrOnLoad : function(xhr)
31907     {
31908         if(this.loadMask){
31909             this.maskEl.unmask();
31910         }
31911         
31912         if (xhr.readyState !== 4) {
31913             this.fireEvent('exception', this, xhr);
31914             return;
31915         }
31916
31917         var response = Roo.decode(xhr.responseText);
31918         
31919         if(!response.success){
31920             this.fireEvent('exception', this, xhr);
31921             return;
31922         }
31923         
31924         var response = Roo.decode(xhr.responseText);
31925         
31926         this.fireEvent('upload', this, response);
31927         
31928     },
31929     
31930     xhrOnError : function()
31931     {
31932         if(this.loadMask){
31933             this.maskEl.unmask();
31934         }
31935         
31936         Roo.log('xhr on error');
31937         
31938         var response = Roo.decode(xhr.responseText);
31939           
31940         Roo.log(response);
31941         
31942     },
31943     
31944     prepare : function(file)
31945     {   
31946         if(this.loadMask){
31947             this.maskEl.mask(this.loadingText);
31948         }
31949         
31950         this.file = false;
31951         this.exif = {};
31952         
31953         if(typeof(file) === 'string'){
31954             this.loadCanvas(file);
31955             return;
31956         }
31957         
31958         if(!file || !this.urlAPI){
31959             return;
31960         }
31961         
31962         this.file = file;
31963         this.cropType = file.type;
31964         
31965         var _this = this;
31966         
31967         if(this.fireEvent('prepare', this, this.file) != false){
31968             
31969             var reader = new FileReader();
31970             
31971             reader.onload = function (e) {
31972                 if (e.target.error) {
31973                     Roo.log(e.target.error);
31974                     return;
31975                 }
31976                 
31977                 var buffer = e.target.result,
31978                     dataView = new DataView(buffer),
31979                     offset = 2,
31980                     maxOffset = dataView.byteLength - 4,
31981                     markerBytes,
31982                     markerLength;
31983                 
31984                 if (dataView.getUint16(0) === 0xffd8) {
31985                     while (offset < maxOffset) {
31986                         markerBytes = dataView.getUint16(offset);
31987                         
31988                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31989                             markerLength = dataView.getUint16(offset + 2) + 2;
31990                             if (offset + markerLength > dataView.byteLength) {
31991                                 Roo.log('Invalid meta data: Invalid segment size.');
31992                                 break;
31993                             }
31994                             
31995                             if(markerBytes == 0xffe1){
31996                                 _this.parseExifData(
31997                                     dataView,
31998                                     offset,
31999                                     markerLength
32000                                 );
32001                             }
32002                             
32003                             offset += markerLength;
32004                             
32005                             continue;
32006                         }
32007                         
32008                         break;
32009                     }
32010                     
32011                 }
32012                 
32013                 var url = _this.urlAPI.createObjectURL(_this.file);
32014                 
32015                 _this.loadCanvas(url);
32016                 
32017                 return;
32018             }
32019             
32020             reader.readAsArrayBuffer(this.file);
32021             
32022         }
32023         
32024     },
32025     
32026     parseExifData : function(dataView, offset, length)
32027     {
32028         var tiffOffset = offset + 10,
32029             littleEndian,
32030             dirOffset;
32031     
32032         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32033             // No Exif data, might be XMP data instead
32034             return;
32035         }
32036         
32037         // Check for the ASCII code for "Exif" (0x45786966):
32038         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32039             // No Exif data, might be XMP data instead
32040             return;
32041         }
32042         if (tiffOffset + 8 > dataView.byteLength) {
32043             Roo.log('Invalid Exif data: Invalid segment size.');
32044             return;
32045         }
32046         // Check for the two null bytes:
32047         if (dataView.getUint16(offset + 8) !== 0x0000) {
32048             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32049             return;
32050         }
32051         // Check the byte alignment:
32052         switch (dataView.getUint16(tiffOffset)) {
32053         case 0x4949:
32054             littleEndian = true;
32055             break;
32056         case 0x4D4D:
32057             littleEndian = false;
32058             break;
32059         default:
32060             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32061             return;
32062         }
32063         // Check for the TIFF tag marker (0x002A):
32064         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32065             Roo.log('Invalid Exif data: Missing TIFF marker.');
32066             return;
32067         }
32068         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32069         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32070         
32071         this.parseExifTags(
32072             dataView,
32073             tiffOffset,
32074             tiffOffset + dirOffset,
32075             littleEndian
32076         );
32077     },
32078     
32079     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32080     {
32081         var tagsNumber,
32082             dirEndOffset,
32083             i;
32084         if (dirOffset + 6 > dataView.byteLength) {
32085             Roo.log('Invalid Exif data: Invalid directory offset.');
32086             return;
32087         }
32088         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32089         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32090         if (dirEndOffset + 4 > dataView.byteLength) {
32091             Roo.log('Invalid Exif data: Invalid directory size.');
32092             return;
32093         }
32094         for (i = 0; i < tagsNumber; i += 1) {
32095             this.parseExifTag(
32096                 dataView,
32097                 tiffOffset,
32098                 dirOffset + 2 + 12 * i, // tag offset
32099                 littleEndian
32100             );
32101         }
32102         // Return the offset to the next directory:
32103         return dataView.getUint32(dirEndOffset, littleEndian);
32104     },
32105     
32106     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32107     {
32108         var tag = dataView.getUint16(offset, littleEndian);
32109         
32110         this.exif[tag] = this.getExifValue(
32111             dataView,
32112             tiffOffset,
32113             offset,
32114             dataView.getUint16(offset + 2, littleEndian), // tag type
32115             dataView.getUint32(offset + 4, littleEndian), // tag length
32116             littleEndian
32117         );
32118     },
32119     
32120     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32121     {
32122         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32123             tagSize,
32124             dataOffset,
32125             values,
32126             i,
32127             str,
32128             c;
32129     
32130         if (!tagType) {
32131             Roo.log('Invalid Exif data: Invalid tag type.');
32132             return;
32133         }
32134         
32135         tagSize = tagType.size * length;
32136         // Determine if the value is contained in the dataOffset bytes,
32137         // or if the value at the dataOffset is a pointer to the actual data:
32138         dataOffset = tagSize > 4 ?
32139                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32140         if (dataOffset + tagSize > dataView.byteLength) {
32141             Roo.log('Invalid Exif data: Invalid data offset.');
32142             return;
32143         }
32144         if (length === 1) {
32145             return tagType.getValue(dataView, dataOffset, littleEndian);
32146         }
32147         values = [];
32148         for (i = 0; i < length; i += 1) {
32149             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32150         }
32151         
32152         if (tagType.ascii) {
32153             str = '';
32154             // Concatenate the chars:
32155             for (i = 0; i < values.length; i += 1) {
32156                 c = values[i];
32157                 // Ignore the terminating NULL byte(s):
32158                 if (c === '\u0000') {
32159                     break;
32160                 }
32161                 str += c;
32162             }
32163             return str;
32164         }
32165         return values;
32166     }
32167     
32168 });
32169
32170 Roo.apply(Roo.bootstrap.UploadCropbox, {
32171     tags : {
32172         'Orientation': 0x0112
32173     },
32174     
32175     Orientation: {
32176             1: 0, //'top-left',
32177 //            2: 'top-right',
32178             3: 180, //'bottom-right',
32179 //            4: 'bottom-left',
32180 //            5: 'left-top',
32181             6: 90, //'right-top',
32182 //            7: 'right-bottom',
32183             8: 270 //'left-bottom'
32184     },
32185     
32186     exifTagTypes : {
32187         // byte, 8-bit unsigned int:
32188         1: {
32189             getValue: function (dataView, dataOffset) {
32190                 return dataView.getUint8(dataOffset);
32191             },
32192             size: 1
32193         },
32194         // ascii, 8-bit byte:
32195         2: {
32196             getValue: function (dataView, dataOffset) {
32197                 return String.fromCharCode(dataView.getUint8(dataOffset));
32198             },
32199             size: 1,
32200             ascii: true
32201         },
32202         // short, 16 bit int:
32203         3: {
32204             getValue: function (dataView, dataOffset, littleEndian) {
32205                 return dataView.getUint16(dataOffset, littleEndian);
32206             },
32207             size: 2
32208         },
32209         // long, 32 bit int:
32210         4: {
32211             getValue: function (dataView, dataOffset, littleEndian) {
32212                 return dataView.getUint32(dataOffset, littleEndian);
32213             },
32214             size: 4
32215         },
32216         // rational = two long values, first is numerator, second is denominator:
32217         5: {
32218             getValue: function (dataView, dataOffset, littleEndian) {
32219                 return dataView.getUint32(dataOffset, littleEndian) /
32220                     dataView.getUint32(dataOffset + 4, littleEndian);
32221             },
32222             size: 8
32223         },
32224         // slong, 32 bit signed int:
32225         9: {
32226             getValue: function (dataView, dataOffset, littleEndian) {
32227                 return dataView.getInt32(dataOffset, littleEndian);
32228             },
32229             size: 4
32230         },
32231         // srational, two slongs, first is numerator, second is denominator:
32232         10: {
32233             getValue: function (dataView, dataOffset, littleEndian) {
32234                 return dataView.getInt32(dataOffset, littleEndian) /
32235                     dataView.getInt32(dataOffset + 4, littleEndian);
32236             },
32237             size: 8
32238         }
32239     },
32240     
32241     footer : {
32242         STANDARD : [
32243             {
32244                 tag : 'div',
32245                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32246                 action : 'rotate-left',
32247                 cn : [
32248                     {
32249                         tag : 'button',
32250                         cls : 'btn btn-default',
32251                         html : '<i class="fa fa-undo"></i>'
32252                     }
32253                 ]
32254             },
32255             {
32256                 tag : 'div',
32257                 cls : 'btn-group roo-upload-cropbox-picture',
32258                 action : 'picture',
32259                 cn : [
32260                     {
32261                         tag : 'button',
32262                         cls : 'btn btn-default',
32263                         html : '<i class="fa fa-picture-o"></i>'
32264                     }
32265                 ]
32266             },
32267             {
32268                 tag : 'div',
32269                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32270                 action : 'rotate-right',
32271                 cn : [
32272                     {
32273                         tag : 'button',
32274                         cls : 'btn btn-default',
32275                         html : '<i class="fa fa-repeat"></i>'
32276                     }
32277                 ]
32278             }
32279         ],
32280         DOCUMENT : [
32281             {
32282                 tag : 'div',
32283                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32284                 action : 'rotate-left',
32285                 cn : [
32286                     {
32287                         tag : 'button',
32288                         cls : 'btn btn-default',
32289                         html : '<i class="fa fa-undo"></i>'
32290                     }
32291                 ]
32292             },
32293             {
32294                 tag : 'div',
32295                 cls : 'btn-group roo-upload-cropbox-download',
32296                 action : 'download',
32297                 cn : [
32298                     {
32299                         tag : 'button',
32300                         cls : 'btn btn-default',
32301                         html : '<i class="fa fa-download"></i>'
32302                     }
32303                 ]
32304             },
32305             {
32306                 tag : 'div',
32307                 cls : 'btn-group roo-upload-cropbox-crop',
32308                 action : 'crop',
32309                 cn : [
32310                     {
32311                         tag : 'button',
32312                         cls : 'btn btn-default',
32313                         html : '<i class="fa fa-crop"></i>'
32314                     }
32315                 ]
32316             },
32317             {
32318                 tag : 'div',
32319                 cls : 'btn-group roo-upload-cropbox-trash',
32320                 action : 'trash',
32321                 cn : [
32322                     {
32323                         tag : 'button',
32324                         cls : 'btn btn-default',
32325                         html : '<i class="fa fa-trash"></i>'
32326                     }
32327                 ]
32328             },
32329             {
32330                 tag : 'div',
32331                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32332                 action : 'rotate-right',
32333                 cn : [
32334                     {
32335                         tag : 'button',
32336                         cls : 'btn btn-default',
32337                         html : '<i class="fa fa-repeat"></i>'
32338                     }
32339                 ]
32340             }
32341         ],
32342         ROTATOR : [
32343             {
32344                 tag : 'div',
32345                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32346                 action : 'rotate-left',
32347                 cn : [
32348                     {
32349                         tag : 'button',
32350                         cls : 'btn btn-default',
32351                         html : '<i class="fa fa-undo"></i>'
32352                     }
32353                 ]
32354             },
32355             {
32356                 tag : 'div',
32357                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32358                 action : 'rotate-right',
32359                 cn : [
32360                     {
32361                         tag : 'button',
32362                         cls : 'btn btn-default',
32363                         html : '<i class="fa fa-repeat"></i>'
32364                     }
32365                 ]
32366             }
32367         ]
32368     }
32369 });
32370
32371 /*
32372 * Licence: LGPL
32373 */
32374
32375 /**
32376  * @class Roo.bootstrap.DocumentManager
32377  * @extends Roo.bootstrap.Component
32378  * Bootstrap DocumentManager class
32379  * @cfg {String} paramName default 'imageUpload'
32380  * @cfg {String} toolTipName default 'filename'
32381  * @cfg {String} method default POST
32382  * @cfg {String} url action url
32383  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32384  * @cfg {Boolean} multiple multiple upload default true
32385  * @cfg {Number} thumbSize default 300
32386  * @cfg {String} fieldLabel
32387  * @cfg {Number} labelWidth default 4
32388  * @cfg {String} labelAlign (left|top) default left
32389  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32390 * @cfg {Number} labellg set the width of label (1-12)
32391  * @cfg {Number} labelmd set the width of label (1-12)
32392  * @cfg {Number} labelsm set the width of label (1-12)
32393  * @cfg {Number} labelxs set the width of label (1-12)
32394  * 
32395  * @constructor
32396  * Create a new DocumentManager
32397  * @param {Object} config The config object
32398  */
32399
32400 Roo.bootstrap.DocumentManager = function(config){
32401     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32402     
32403     this.files = [];
32404     this.delegates = [];
32405     
32406     this.addEvents({
32407         /**
32408          * @event initial
32409          * Fire when initial the DocumentManager
32410          * @param {Roo.bootstrap.DocumentManager} this
32411          */
32412         "initial" : true,
32413         /**
32414          * @event inspect
32415          * inspect selected file
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          * @param {File} file
32418          */
32419         "inspect" : true,
32420         /**
32421          * @event exception
32422          * Fire when xhr load exception
32423          * @param {Roo.bootstrap.DocumentManager} this
32424          * @param {XMLHttpRequest} xhr
32425          */
32426         "exception" : true,
32427         /**
32428          * @event afterupload
32429          * Fire when xhr load exception
32430          * @param {Roo.bootstrap.DocumentManager} this
32431          * @param {XMLHttpRequest} xhr
32432          */
32433         "afterupload" : true,
32434         /**
32435          * @event prepare
32436          * prepare the form data
32437          * @param {Roo.bootstrap.DocumentManager} this
32438          * @param {Object} formData
32439          */
32440         "prepare" : true,
32441         /**
32442          * @event remove
32443          * Fire when remove the file
32444          * @param {Roo.bootstrap.DocumentManager} this
32445          * @param {Object} file
32446          */
32447         "remove" : true,
32448         /**
32449          * @event refresh
32450          * Fire after refresh the file
32451          * @param {Roo.bootstrap.DocumentManager} this
32452          */
32453         "refresh" : true,
32454         /**
32455          * @event click
32456          * Fire after click the image
32457          * @param {Roo.bootstrap.DocumentManager} this
32458          * @param {Object} file
32459          */
32460         "click" : true,
32461         /**
32462          * @event edit
32463          * Fire when upload a image and editable set to true
32464          * @param {Roo.bootstrap.DocumentManager} this
32465          * @param {Object} file
32466          */
32467         "edit" : true,
32468         /**
32469          * @event beforeselectfile
32470          * Fire before select file
32471          * @param {Roo.bootstrap.DocumentManager} this
32472          */
32473         "beforeselectfile" : true,
32474         /**
32475          * @event process
32476          * Fire before process file
32477          * @param {Roo.bootstrap.DocumentManager} this
32478          * @param {Object} file
32479          */
32480         "process" : true,
32481         /**
32482          * @event previewrendered
32483          * Fire when preview rendered
32484          * @param {Roo.bootstrap.DocumentManager} this
32485          * @param {Object} file
32486          */
32487         "previewrendered" : true,
32488         /**
32489          */
32490         "previewResize" : true
32491         
32492     });
32493 };
32494
32495 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32496     
32497     boxes : 0,
32498     inputName : '',
32499     thumbSize : 300,
32500     multiple : true,
32501     files : false,
32502     method : 'POST',
32503     url : '',
32504     paramName : 'imageUpload',
32505     toolTipName : 'filename',
32506     fieldLabel : '',
32507     labelWidth : 4,
32508     labelAlign : 'left',
32509     editable : true,
32510     delegates : false,
32511     xhr : false, 
32512     
32513     labellg : 0,
32514     labelmd : 0,
32515     labelsm : 0,
32516     labelxs : 0,
32517     
32518     getAutoCreate : function()
32519     {   
32520         var managerWidget = {
32521             tag : 'div',
32522             cls : 'roo-document-manager',
32523             cn : [
32524                 {
32525                     tag : 'input',
32526                     cls : 'roo-document-manager-selector',
32527                     type : 'file'
32528                 },
32529                 {
32530                     tag : 'div',
32531                     cls : 'roo-document-manager-uploader',
32532                     cn : [
32533                         {
32534                             tag : 'div',
32535                             cls : 'roo-document-manager-upload-btn',
32536                             html : '<i class="fa fa-plus"></i>'
32537                         }
32538                     ]
32539                     
32540                 }
32541             ]
32542         };
32543         
32544         var content = [
32545             {
32546                 tag : 'div',
32547                 cls : 'column col-md-12',
32548                 cn : managerWidget
32549             }
32550         ];
32551         
32552         if(this.fieldLabel.length){
32553             
32554             content = [
32555                 {
32556                     tag : 'div',
32557                     cls : 'column col-md-12',
32558                     html : this.fieldLabel
32559                 },
32560                 {
32561                     tag : 'div',
32562                     cls : 'column col-md-12',
32563                     cn : managerWidget
32564                 }
32565             ];
32566
32567             if(this.labelAlign == 'left'){
32568                 content = [
32569                     {
32570                         tag : 'div',
32571                         cls : 'column',
32572                         html : this.fieldLabel
32573                     },
32574                     {
32575                         tag : 'div',
32576                         cls : 'column',
32577                         cn : managerWidget
32578                     }
32579                 ];
32580                 
32581                 if(this.labelWidth > 12){
32582                     content[0].style = "width: " + this.labelWidth + 'px';
32583                 }
32584
32585                 if(this.labelWidth < 13 && this.labelmd == 0){
32586                     this.labelmd = this.labelWidth;
32587                 }
32588
32589                 if(this.labellg > 0){
32590                     content[0].cls += ' col-lg-' + this.labellg;
32591                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32592                 }
32593
32594                 if(this.labelmd > 0){
32595                     content[0].cls += ' col-md-' + this.labelmd;
32596                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32597                 }
32598
32599                 if(this.labelsm > 0){
32600                     content[0].cls += ' col-sm-' + this.labelsm;
32601                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32602                 }
32603
32604                 if(this.labelxs > 0){
32605                     content[0].cls += ' col-xs-' + this.labelxs;
32606                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32607                 }
32608                 
32609             }
32610         }
32611         
32612         var cfg = {
32613             tag : 'div',
32614             cls : 'row clearfix',
32615             cn : content
32616         };
32617         
32618         return cfg;
32619         
32620     },
32621     
32622     initEvents : function()
32623     {
32624         this.managerEl = this.el.select('.roo-document-manager', true).first();
32625         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32626         
32627         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32628         this.selectorEl.hide();
32629         
32630         if(this.multiple){
32631             this.selectorEl.attr('multiple', 'multiple');
32632         }
32633         
32634         this.selectorEl.on('change', this.onFileSelected, this);
32635         
32636         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32637         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32638         
32639         this.uploader.on('click', this.onUploaderClick, this);
32640         
32641         this.renderProgressDialog();
32642         
32643         var _this = this;
32644         
32645         window.addEventListener("resize", function() { _this.refresh(); } );
32646         
32647         this.fireEvent('initial', this);
32648     },
32649     
32650     renderProgressDialog : function()
32651     {
32652         var _this = this;
32653         
32654         this.progressDialog = new Roo.bootstrap.Modal({
32655             cls : 'roo-document-manager-progress-dialog',
32656             allow_close : false,
32657             animate : false,
32658             title : '',
32659             buttons : [
32660                 {
32661                     name  :'cancel',
32662                     weight : 'danger',
32663                     html : 'Cancel'
32664                 }
32665             ], 
32666             listeners : { 
32667                 btnclick : function() {
32668                     _this.uploadCancel();
32669                     this.hide();
32670                 }
32671             }
32672         });
32673          
32674         this.progressDialog.render(Roo.get(document.body));
32675          
32676         this.progress = new Roo.bootstrap.Progress({
32677             cls : 'roo-document-manager-progress',
32678             active : true,
32679             striped : true
32680         });
32681         
32682         this.progress.render(this.progressDialog.getChildContainer());
32683         
32684         this.progressBar = new Roo.bootstrap.ProgressBar({
32685             cls : 'roo-document-manager-progress-bar',
32686             aria_valuenow : 0,
32687             aria_valuemin : 0,
32688             aria_valuemax : 12,
32689             panel : 'success'
32690         });
32691         
32692         this.progressBar.render(this.progress.getChildContainer());
32693     },
32694     
32695     onUploaderClick : function(e)
32696     {
32697         e.preventDefault();
32698      
32699         if(this.fireEvent('beforeselectfile', this) != false){
32700             this.selectorEl.dom.click();
32701         }
32702         
32703     },
32704     
32705     onFileSelected : function(e)
32706     {
32707         e.preventDefault();
32708         
32709         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32710             return;
32711         }
32712         
32713         Roo.each(this.selectorEl.dom.files, function(file){
32714             if(this.fireEvent('inspect', this, file) != false){
32715                 this.files.push(file);
32716             }
32717         }, this);
32718         
32719         this.queue();
32720         
32721     },
32722     
32723     queue : function()
32724     {
32725         this.selectorEl.dom.value = '';
32726         
32727         if(!this.files || !this.files.length){
32728             return;
32729         }
32730         
32731         if(this.boxes > 0 && this.files.length > this.boxes){
32732             this.files = this.files.slice(0, this.boxes);
32733         }
32734         
32735         this.uploader.show();
32736         
32737         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32738             this.uploader.hide();
32739         }
32740         
32741         var _this = this;
32742         
32743         var files = [];
32744         
32745         var docs = [];
32746         
32747         Roo.each(this.files, function(file){
32748             
32749             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32750                 var f = this.renderPreview(file);
32751                 files.push(f);
32752                 return;
32753             }
32754             
32755             if(file.type.indexOf('image') != -1){
32756                 this.delegates.push(
32757                     (function(){
32758                         _this.process(file);
32759                     }).createDelegate(this)
32760                 );
32761         
32762                 return;
32763             }
32764             
32765             docs.push(
32766                 (function(){
32767                     _this.process(file);
32768                 }).createDelegate(this)
32769             );
32770             
32771         }, this);
32772         
32773         this.files = files;
32774         
32775         this.delegates = this.delegates.concat(docs);
32776         
32777         if(!this.delegates.length){
32778             this.refresh();
32779             return;
32780         }
32781         
32782         this.progressBar.aria_valuemax = this.delegates.length;
32783         
32784         this.arrange();
32785         
32786         return;
32787     },
32788     
32789     arrange : function()
32790     {
32791         if(!this.delegates.length){
32792             this.progressDialog.hide();
32793             this.refresh();
32794             return;
32795         }
32796         
32797         var delegate = this.delegates.shift();
32798         
32799         this.progressDialog.show();
32800         
32801         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32802         
32803         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32804         
32805         delegate();
32806     },
32807     
32808     refresh : function()
32809     {
32810         this.uploader.show();
32811         
32812         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32813             this.uploader.hide();
32814         }
32815         
32816         Roo.isTouch ? this.closable(false) : this.closable(true);
32817         
32818         this.fireEvent('refresh', this);
32819     },
32820     
32821     onRemove : function(e, el, o)
32822     {
32823         e.preventDefault();
32824         
32825         this.fireEvent('remove', this, o);
32826         
32827     },
32828     
32829     remove : function(o)
32830     {
32831         var files = [];
32832         
32833         Roo.each(this.files, function(file){
32834             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32835                 files.push(file);
32836                 return;
32837             }
32838
32839             o.target.remove();
32840
32841         }, this);
32842         
32843         this.files = files;
32844         
32845         this.refresh();
32846     },
32847     
32848     clear : function()
32849     {
32850         Roo.each(this.files, function(file){
32851             if(!file.target){
32852                 return;
32853             }
32854             
32855             file.target.remove();
32856
32857         }, this);
32858         
32859         this.files = [];
32860         
32861         this.refresh();
32862     },
32863     
32864     onClick : function(e, el, o)
32865     {
32866         e.preventDefault();
32867         
32868         this.fireEvent('click', this, o);
32869         
32870     },
32871     
32872     closable : function(closable)
32873     {
32874         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32875             
32876             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32877             
32878             if(closable){
32879                 el.show();
32880                 return;
32881             }
32882             
32883             el.hide();
32884             
32885         }, this);
32886     },
32887     
32888     xhrOnLoad : function(xhr)
32889     {
32890         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32891             el.remove();
32892         }, this);
32893         
32894         if (xhr.readyState !== 4) {
32895             this.arrange();
32896             this.fireEvent('exception', this, xhr);
32897             return;
32898         }
32899
32900         var response = Roo.decode(xhr.responseText);
32901         
32902         if(!response.success){
32903             this.arrange();
32904             this.fireEvent('exception', this, xhr);
32905             return;
32906         }
32907         
32908         var file = this.renderPreview(response.data);
32909         
32910         this.files.push(file);
32911         
32912         this.arrange();
32913         
32914         this.fireEvent('afterupload', this, xhr);
32915         
32916     },
32917     
32918     xhrOnError : function(xhr)
32919     {
32920         Roo.log('xhr on error');
32921         
32922         var response = Roo.decode(xhr.responseText);
32923           
32924         Roo.log(response);
32925         
32926         this.arrange();
32927     },
32928     
32929     process : function(file)
32930     {
32931         if(this.fireEvent('process', this, file) !== false){
32932             if(this.editable && file.type.indexOf('image') != -1){
32933                 this.fireEvent('edit', this, file);
32934                 return;
32935             }
32936
32937             this.uploadStart(file, false);
32938
32939             return;
32940         }
32941         
32942     },
32943     
32944     uploadStart : function(file, crop)
32945     {
32946         this.xhr = new XMLHttpRequest();
32947         
32948         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32949             this.arrange();
32950             return;
32951         }
32952         
32953         file.xhr = this.xhr;
32954             
32955         this.managerEl.createChild({
32956             tag : 'div',
32957             cls : 'roo-document-manager-loading',
32958             cn : [
32959                 {
32960                     tag : 'div',
32961                     tooltip : file.name,
32962                     cls : 'roo-document-manager-thumb',
32963                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32964                 }
32965             ]
32966
32967         });
32968
32969         this.xhr.open(this.method, this.url, true);
32970         
32971         var headers = {
32972             "Accept": "application/json",
32973             "Cache-Control": "no-cache",
32974             "X-Requested-With": "XMLHttpRequest"
32975         };
32976         
32977         for (var headerName in headers) {
32978             var headerValue = headers[headerName];
32979             if (headerValue) {
32980                 this.xhr.setRequestHeader(headerName, headerValue);
32981             }
32982         }
32983         
32984         var _this = this;
32985         
32986         this.xhr.onload = function()
32987         {
32988             _this.xhrOnLoad(_this.xhr);
32989         }
32990         
32991         this.xhr.onerror = function()
32992         {
32993             _this.xhrOnError(_this.xhr);
32994         }
32995         
32996         var formData = new FormData();
32997
32998         formData.append('returnHTML', 'NO');
32999         
33000         if(crop){
33001             formData.append('crop', crop);
33002         }
33003         
33004         formData.append(this.paramName, file, file.name);
33005         
33006         var options = {
33007             file : file, 
33008             manually : false
33009         };
33010         
33011         if(this.fireEvent('prepare', this, formData, options) != false){
33012             
33013             if(options.manually){
33014                 return;
33015             }
33016             
33017             this.xhr.send(formData);
33018             return;
33019         };
33020         
33021         this.uploadCancel();
33022     },
33023     
33024     uploadCancel : function()
33025     {
33026         if (this.xhr) {
33027             this.xhr.abort();
33028         }
33029         
33030         this.delegates = [];
33031         
33032         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33033             el.remove();
33034         }, this);
33035         
33036         this.arrange();
33037     },
33038     
33039     renderPreview : function(file)
33040     {
33041         if(typeof(file.target) != 'undefined' && file.target){
33042             return file;
33043         }
33044         
33045         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33046         
33047         var previewEl = this.managerEl.createChild({
33048             tag : 'div',
33049             cls : 'roo-document-manager-preview',
33050             cn : [
33051                 {
33052                     tag : 'div',
33053                     tooltip : file[this.toolTipName],
33054                     cls : 'roo-document-manager-thumb',
33055                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33056                 },
33057                 {
33058                     tag : 'button',
33059                     cls : 'close',
33060                     html : '<i class="fa fa-times-circle"></i>'
33061                 }
33062             ]
33063         });
33064
33065         var close = previewEl.select('button.close', true).first();
33066
33067         close.on('click', this.onRemove, this, file);
33068
33069         file.target = previewEl;
33070
33071         var image = previewEl.select('img', true).first();
33072         
33073         var _this = this;
33074         
33075         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33076         
33077         image.on('click', this.onClick, this, file);
33078         
33079         this.fireEvent('previewrendered', this, file);
33080         
33081         return file;
33082         
33083     },
33084     
33085     onPreviewLoad : function(file, image)
33086     {
33087         if(typeof(file.target) == 'undefined' || !file.target){
33088             return;
33089         }
33090         
33091         var width = image.dom.naturalWidth || image.dom.width;
33092         var height = image.dom.naturalHeight || image.dom.height;
33093         
33094         if(!this.previewResize) {
33095             return;
33096         }
33097         
33098         if(width > height){
33099             file.target.addClass('wide');
33100             return;
33101         }
33102         
33103         file.target.addClass('tall');
33104         return;
33105         
33106     },
33107     
33108     uploadFromSource : function(file, crop)
33109     {
33110         this.xhr = new XMLHttpRequest();
33111         
33112         this.managerEl.createChild({
33113             tag : 'div',
33114             cls : 'roo-document-manager-loading',
33115             cn : [
33116                 {
33117                     tag : 'div',
33118                     tooltip : file.name,
33119                     cls : 'roo-document-manager-thumb',
33120                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33121                 }
33122             ]
33123
33124         });
33125
33126         this.xhr.open(this.method, this.url, true);
33127         
33128         var headers = {
33129             "Accept": "application/json",
33130             "Cache-Control": "no-cache",
33131             "X-Requested-With": "XMLHttpRequest"
33132         };
33133         
33134         for (var headerName in headers) {
33135             var headerValue = headers[headerName];
33136             if (headerValue) {
33137                 this.xhr.setRequestHeader(headerName, headerValue);
33138             }
33139         }
33140         
33141         var _this = this;
33142         
33143         this.xhr.onload = function()
33144         {
33145             _this.xhrOnLoad(_this.xhr);
33146         }
33147         
33148         this.xhr.onerror = function()
33149         {
33150             _this.xhrOnError(_this.xhr);
33151         }
33152         
33153         var formData = new FormData();
33154
33155         formData.append('returnHTML', 'NO');
33156         
33157         formData.append('crop', crop);
33158         
33159         if(typeof(file.filename) != 'undefined'){
33160             formData.append('filename', file.filename);
33161         }
33162         
33163         if(typeof(file.mimetype) != 'undefined'){
33164             formData.append('mimetype', file.mimetype);
33165         }
33166         
33167         Roo.log(formData);
33168         
33169         if(this.fireEvent('prepare', this, formData) != false){
33170             this.xhr.send(formData);
33171         };
33172     }
33173 });
33174
33175 /*
33176 * Licence: LGPL
33177 */
33178
33179 /**
33180  * @class Roo.bootstrap.DocumentViewer
33181  * @extends Roo.bootstrap.Component
33182  * Bootstrap DocumentViewer class
33183  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33184  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33185  * 
33186  * @constructor
33187  * Create a new DocumentViewer
33188  * @param {Object} config The config object
33189  */
33190
33191 Roo.bootstrap.DocumentViewer = function(config){
33192     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33193     
33194     this.addEvents({
33195         /**
33196          * @event initial
33197          * Fire after initEvent
33198          * @param {Roo.bootstrap.DocumentViewer} this
33199          */
33200         "initial" : true,
33201         /**
33202          * @event click
33203          * Fire after click
33204          * @param {Roo.bootstrap.DocumentViewer} this
33205          */
33206         "click" : true,
33207         /**
33208          * @event download
33209          * Fire after download button
33210          * @param {Roo.bootstrap.DocumentViewer} this
33211          */
33212         "download" : true,
33213         /**
33214          * @event trash
33215          * Fire after trash button
33216          * @param {Roo.bootstrap.DocumentViewer} this
33217          */
33218         "trash" : true
33219         
33220     });
33221 };
33222
33223 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33224     
33225     showDownload : true,
33226     
33227     showTrash : true,
33228     
33229     getAutoCreate : function()
33230     {
33231         var cfg = {
33232             tag : 'div',
33233             cls : 'roo-document-viewer',
33234             cn : [
33235                 {
33236                     tag : 'div',
33237                     cls : 'roo-document-viewer-body',
33238                     cn : [
33239                         {
33240                             tag : 'div',
33241                             cls : 'roo-document-viewer-thumb',
33242                             cn : [
33243                                 {
33244                                     tag : 'img',
33245                                     cls : 'roo-document-viewer-image'
33246                                 }
33247                             ]
33248                         }
33249                     ]
33250                 },
33251                 {
33252                     tag : 'div',
33253                     cls : 'roo-document-viewer-footer',
33254                     cn : {
33255                         tag : 'div',
33256                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33257                         cn : [
33258                             {
33259                                 tag : 'div',
33260                                 cls : 'btn-group roo-document-viewer-download',
33261                                 cn : [
33262                                     {
33263                                         tag : 'button',
33264                                         cls : 'btn btn-default',
33265                                         html : '<i class="fa fa-download"></i>'
33266                                     }
33267                                 ]
33268                             },
33269                             {
33270                                 tag : 'div',
33271                                 cls : 'btn-group roo-document-viewer-trash',
33272                                 cn : [
33273                                     {
33274                                         tag : 'button',
33275                                         cls : 'btn btn-default',
33276                                         html : '<i class="fa fa-trash"></i>'
33277                                     }
33278                                 ]
33279                             }
33280                         ]
33281                     }
33282                 }
33283             ]
33284         };
33285         
33286         return cfg;
33287     },
33288     
33289     initEvents : function()
33290     {
33291         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33292         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33293         
33294         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33295         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33296         
33297         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33298         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33299         
33300         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33301         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33302         
33303         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33304         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33305         
33306         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33307         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33308         
33309         this.bodyEl.on('click', this.onClick, this);
33310         this.downloadBtn.on('click', this.onDownload, this);
33311         this.trashBtn.on('click', this.onTrash, this);
33312         
33313         this.downloadBtn.hide();
33314         this.trashBtn.hide();
33315         
33316         if(this.showDownload){
33317             this.downloadBtn.show();
33318         }
33319         
33320         if(this.showTrash){
33321             this.trashBtn.show();
33322         }
33323         
33324         if(!this.showDownload && !this.showTrash) {
33325             this.footerEl.hide();
33326         }
33327         
33328     },
33329     
33330     initial : function()
33331     {
33332         this.fireEvent('initial', this);
33333         
33334     },
33335     
33336     onClick : function(e)
33337     {
33338         e.preventDefault();
33339         
33340         this.fireEvent('click', this);
33341     },
33342     
33343     onDownload : function(e)
33344     {
33345         e.preventDefault();
33346         
33347         this.fireEvent('download', this);
33348     },
33349     
33350     onTrash : function(e)
33351     {
33352         e.preventDefault();
33353         
33354         this.fireEvent('trash', this);
33355     }
33356     
33357 });
33358 /*
33359  * - LGPL
33360  *
33361  * FieldLabel
33362  * 
33363  */
33364
33365 /**
33366  * @class Roo.bootstrap.FieldLabel
33367  * @extends Roo.bootstrap.Component
33368  * Bootstrap FieldLabel class
33369  * @cfg {String} html contents of the element
33370  * @cfg {String} tag tag of the element default label
33371  * @cfg {String} cls class of the element
33372  * @cfg {String} target label target 
33373  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33374  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33375  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33376  * @cfg {String} iconTooltip default "This field is required"
33377  * @cfg {String} indicatorpos (left|right) default left
33378  * 
33379  * @constructor
33380  * Create a new FieldLabel
33381  * @param {Object} config The config object
33382  */
33383
33384 Roo.bootstrap.FieldLabel = function(config){
33385     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33386     
33387     this.addEvents({
33388             /**
33389              * @event invalid
33390              * Fires after the field has been marked as invalid.
33391              * @param {Roo.form.FieldLabel} this
33392              * @param {String} msg The validation message
33393              */
33394             invalid : true,
33395             /**
33396              * @event valid
33397              * Fires after the field has been validated with no errors.
33398              * @param {Roo.form.FieldLabel} this
33399              */
33400             valid : true
33401         });
33402 };
33403
33404 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33405     
33406     tag: 'label',
33407     cls: '',
33408     html: '',
33409     target: '',
33410     allowBlank : true,
33411     invalidClass : 'has-warning',
33412     validClass : 'has-success',
33413     iconTooltip : 'This field is required',
33414     indicatorpos : 'left',
33415     
33416     getAutoCreate : function(){
33417         
33418         var cls = "";
33419         if (!this.allowBlank) {
33420             cls  = "visible";
33421         }
33422         
33423         var cfg = {
33424             tag : this.tag,
33425             cls : 'roo-bootstrap-field-label ' + this.cls,
33426             for : this.target,
33427             cn : [
33428                 {
33429                     tag : 'i',
33430                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33431                     tooltip : this.iconTooltip
33432                 },
33433                 {
33434                     tag : 'span',
33435                     html : this.html
33436                 }
33437             ] 
33438         };
33439         
33440         if(this.indicatorpos == 'right'){
33441             var cfg = {
33442                 tag : this.tag,
33443                 cls : 'roo-bootstrap-field-label ' + this.cls,
33444                 for : this.target,
33445                 cn : [
33446                     {
33447                         tag : 'span',
33448                         html : this.html
33449                     },
33450                     {
33451                         tag : 'i',
33452                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33453                         tooltip : this.iconTooltip
33454                     }
33455                 ] 
33456             };
33457         }
33458         
33459         return cfg;
33460     },
33461     
33462     initEvents: function() 
33463     {
33464         Roo.bootstrap.Element.superclass.initEvents.call(this);
33465         
33466         this.indicator = this.indicatorEl();
33467         
33468         if(this.indicator){
33469             this.indicator.removeClass('visible');
33470             this.indicator.addClass('invisible');
33471         }
33472         
33473         Roo.bootstrap.FieldLabel.register(this);
33474     },
33475     
33476     indicatorEl : function()
33477     {
33478         var indicator = this.el.select('i.roo-required-indicator',true).first();
33479         
33480         if(!indicator){
33481             return false;
33482         }
33483         
33484         return indicator;
33485         
33486     },
33487     
33488     /**
33489      * Mark this field as valid
33490      */
33491     markValid : function()
33492     {
33493         if(this.indicator){
33494             this.indicator.removeClass('visible');
33495             this.indicator.addClass('invisible');
33496         }
33497         if (Roo.bootstrap.version == 3) {
33498             this.el.removeClass(this.invalidClass);
33499             this.el.addClass(this.validClass);
33500         } else {
33501             this.el.removeClass('is-invalid');
33502             this.el.addClass('is-valid');
33503         }
33504         
33505         
33506         this.fireEvent('valid', this);
33507     },
33508     
33509     /**
33510      * Mark this field as invalid
33511      * @param {String} msg The validation message
33512      */
33513     markInvalid : function(msg)
33514     {
33515         if(this.indicator){
33516             this.indicator.removeClass('invisible');
33517             this.indicator.addClass('visible');
33518         }
33519           if (Roo.bootstrap.version == 3) {
33520             this.el.removeClass(this.validClass);
33521             this.el.addClass(this.invalidClass);
33522         } else {
33523             this.el.removeClass('is-valid');
33524             this.el.addClass('is-invalid');
33525         }
33526         
33527         
33528         this.fireEvent('invalid', this, msg);
33529     }
33530     
33531    
33532 });
33533
33534 Roo.apply(Roo.bootstrap.FieldLabel, {
33535     
33536     groups: {},
33537     
33538      /**
33539     * register a FieldLabel Group
33540     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33541     */
33542     register : function(label)
33543     {
33544         if(this.groups.hasOwnProperty(label.target)){
33545             return;
33546         }
33547      
33548         this.groups[label.target] = label;
33549         
33550     },
33551     /**
33552     * fetch a FieldLabel Group based on the target
33553     * @param {string} target
33554     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33555     */
33556     get: function(target) {
33557         if (typeof(this.groups[target]) == 'undefined') {
33558             return false;
33559         }
33560         
33561         return this.groups[target] ;
33562     }
33563 });
33564
33565  
33566
33567  /*
33568  * - LGPL
33569  *
33570  * page DateSplitField.
33571  * 
33572  */
33573
33574
33575 /**
33576  * @class Roo.bootstrap.DateSplitField
33577  * @extends Roo.bootstrap.Component
33578  * Bootstrap DateSplitField class
33579  * @cfg {string} fieldLabel - the label associated
33580  * @cfg {Number} labelWidth set the width of label (0-12)
33581  * @cfg {String} labelAlign (top|left)
33582  * @cfg {Boolean} dayAllowBlank (true|false) default false
33583  * @cfg {Boolean} monthAllowBlank (true|false) default false
33584  * @cfg {Boolean} yearAllowBlank (true|false) default false
33585  * @cfg {string} dayPlaceholder 
33586  * @cfg {string} monthPlaceholder
33587  * @cfg {string} yearPlaceholder
33588  * @cfg {string} dayFormat default 'd'
33589  * @cfg {string} monthFormat default 'm'
33590  * @cfg {string} yearFormat default 'Y'
33591  * @cfg {Number} labellg set the width of label (1-12)
33592  * @cfg {Number} labelmd set the width of label (1-12)
33593  * @cfg {Number} labelsm set the width of label (1-12)
33594  * @cfg {Number} labelxs set the width of label (1-12)
33595
33596  *     
33597  * @constructor
33598  * Create a new DateSplitField
33599  * @param {Object} config The config object
33600  */
33601
33602 Roo.bootstrap.DateSplitField = function(config){
33603     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33604     
33605     this.addEvents({
33606         // raw events
33607          /**
33608          * @event years
33609          * getting the data of years
33610          * @param {Roo.bootstrap.DateSplitField} this
33611          * @param {Object} years
33612          */
33613         "years" : true,
33614         /**
33615          * @event days
33616          * getting the data of days
33617          * @param {Roo.bootstrap.DateSplitField} this
33618          * @param {Object} days
33619          */
33620         "days" : true,
33621         /**
33622          * @event invalid
33623          * Fires after the field has been marked as invalid.
33624          * @param {Roo.form.Field} this
33625          * @param {String} msg The validation message
33626          */
33627         invalid : true,
33628        /**
33629          * @event valid
33630          * Fires after the field has been validated with no errors.
33631          * @param {Roo.form.Field} this
33632          */
33633         valid : true
33634     });
33635 };
33636
33637 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33638     
33639     fieldLabel : '',
33640     labelAlign : 'top',
33641     labelWidth : 3,
33642     dayAllowBlank : false,
33643     monthAllowBlank : false,
33644     yearAllowBlank : false,
33645     dayPlaceholder : '',
33646     monthPlaceholder : '',
33647     yearPlaceholder : '',
33648     dayFormat : 'd',
33649     monthFormat : 'm',
33650     yearFormat : 'Y',
33651     isFormField : true,
33652     labellg : 0,
33653     labelmd : 0,
33654     labelsm : 0,
33655     labelxs : 0,
33656     
33657     getAutoCreate : function()
33658     {
33659         var cfg = {
33660             tag : 'div',
33661             cls : 'row roo-date-split-field-group',
33662             cn : [
33663                 {
33664                     tag : 'input',
33665                     type : 'hidden',
33666                     cls : 'form-hidden-field roo-date-split-field-group-value',
33667                     name : this.name
33668                 }
33669             ]
33670         };
33671         
33672         var labelCls = 'col-md-12';
33673         var contentCls = 'col-md-4';
33674         
33675         if(this.fieldLabel){
33676             
33677             var label = {
33678                 tag : 'div',
33679                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33680                 cn : [
33681                     {
33682                         tag : 'label',
33683                         html : this.fieldLabel
33684                     }
33685                 ]
33686             };
33687             
33688             if(this.labelAlign == 'left'){
33689             
33690                 if(this.labelWidth > 12){
33691                     label.style = "width: " + this.labelWidth + 'px';
33692                 }
33693
33694                 if(this.labelWidth < 13 && this.labelmd == 0){
33695                     this.labelmd = this.labelWidth;
33696                 }
33697
33698                 if(this.labellg > 0){
33699                     labelCls = ' col-lg-' + this.labellg;
33700                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33701                 }
33702
33703                 if(this.labelmd > 0){
33704                     labelCls = ' col-md-' + this.labelmd;
33705                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33706                 }
33707
33708                 if(this.labelsm > 0){
33709                     labelCls = ' col-sm-' + this.labelsm;
33710                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33711                 }
33712
33713                 if(this.labelxs > 0){
33714                     labelCls = ' col-xs-' + this.labelxs;
33715                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33716                 }
33717             }
33718             
33719             label.cls += ' ' + labelCls;
33720             
33721             cfg.cn.push(label);
33722         }
33723         
33724         Roo.each(['day', 'month', 'year'], function(t){
33725             cfg.cn.push({
33726                 tag : 'div',
33727                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33728             });
33729         }, this);
33730         
33731         return cfg;
33732     },
33733     
33734     inputEl: function ()
33735     {
33736         return this.el.select('.roo-date-split-field-group-value', true).first();
33737     },
33738     
33739     onRender : function(ct, position) 
33740     {
33741         var _this = this;
33742         
33743         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33744         
33745         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33746         
33747         this.dayField = new Roo.bootstrap.ComboBox({
33748             allowBlank : this.dayAllowBlank,
33749             alwaysQuery : true,
33750             displayField : 'value',
33751             editable : false,
33752             fieldLabel : '',
33753             forceSelection : true,
33754             mode : 'local',
33755             placeholder : this.dayPlaceholder,
33756             selectOnFocus : true,
33757             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33758             triggerAction : 'all',
33759             typeAhead : true,
33760             valueField : 'value',
33761             store : new Roo.data.SimpleStore({
33762                 data : (function() {    
33763                     var days = [];
33764                     _this.fireEvent('days', _this, days);
33765                     return days;
33766                 })(),
33767                 fields : [ 'value' ]
33768             }),
33769             listeners : {
33770                 select : function (_self, record, index)
33771                 {
33772                     _this.setValue(_this.getValue());
33773                 }
33774             }
33775         });
33776
33777         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33778         
33779         this.monthField = new Roo.bootstrap.MonthField({
33780             after : '<i class=\"fa fa-calendar\"></i>',
33781             allowBlank : this.monthAllowBlank,
33782             placeholder : this.monthPlaceholder,
33783             readOnly : true,
33784             listeners : {
33785                 render : function (_self)
33786                 {
33787                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33788                         e.preventDefault();
33789                         _self.focus();
33790                     });
33791                 },
33792                 select : function (_self, oldvalue, newvalue)
33793                 {
33794                     _this.setValue(_this.getValue());
33795                 }
33796             }
33797         });
33798         
33799         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33800         
33801         this.yearField = new Roo.bootstrap.ComboBox({
33802             allowBlank : this.yearAllowBlank,
33803             alwaysQuery : true,
33804             displayField : 'value',
33805             editable : false,
33806             fieldLabel : '',
33807             forceSelection : true,
33808             mode : 'local',
33809             placeholder : this.yearPlaceholder,
33810             selectOnFocus : true,
33811             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33812             triggerAction : 'all',
33813             typeAhead : true,
33814             valueField : 'value',
33815             store : new Roo.data.SimpleStore({
33816                 data : (function() {
33817                     var years = [];
33818                     _this.fireEvent('years', _this, years);
33819                     return years;
33820                 })(),
33821                 fields : [ 'value' ]
33822             }),
33823             listeners : {
33824                 select : function (_self, record, index)
33825                 {
33826                     _this.setValue(_this.getValue());
33827                 }
33828             }
33829         });
33830
33831         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33832     },
33833     
33834     setValue : function(v, format)
33835     {
33836         this.inputEl.dom.value = v;
33837         
33838         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33839         
33840         var d = Date.parseDate(v, f);
33841         
33842         if(!d){
33843             this.validate();
33844             return;
33845         }
33846         
33847         this.setDay(d.format(this.dayFormat));
33848         this.setMonth(d.format(this.monthFormat));
33849         this.setYear(d.format(this.yearFormat));
33850         
33851         this.validate();
33852         
33853         return;
33854     },
33855     
33856     setDay : function(v)
33857     {
33858         this.dayField.setValue(v);
33859         this.inputEl.dom.value = this.getValue();
33860         this.validate();
33861         return;
33862     },
33863     
33864     setMonth : function(v)
33865     {
33866         this.monthField.setValue(v, true);
33867         this.inputEl.dom.value = this.getValue();
33868         this.validate();
33869         return;
33870     },
33871     
33872     setYear : function(v)
33873     {
33874         this.yearField.setValue(v);
33875         this.inputEl.dom.value = this.getValue();
33876         this.validate();
33877         return;
33878     },
33879     
33880     getDay : function()
33881     {
33882         return this.dayField.getValue();
33883     },
33884     
33885     getMonth : function()
33886     {
33887         return this.monthField.getValue();
33888     },
33889     
33890     getYear : function()
33891     {
33892         return this.yearField.getValue();
33893     },
33894     
33895     getValue : function()
33896     {
33897         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33898         
33899         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33900         
33901         return date;
33902     },
33903     
33904     reset : function()
33905     {
33906         this.setDay('');
33907         this.setMonth('');
33908         this.setYear('');
33909         this.inputEl.dom.value = '';
33910         this.validate();
33911         return;
33912     },
33913     
33914     validate : function()
33915     {
33916         var d = this.dayField.validate();
33917         var m = this.monthField.validate();
33918         var y = this.yearField.validate();
33919         
33920         var valid = true;
33921         
33922         if(
33923                 (!this.dayAllowBlank && !d) ||
33924                 (!this.monthAllowBlank && !m) ||
33925                 (!this.yearAllowBlank && !y)
33926         ){
33927             valid = false;
33928         }
33929         
33930         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33931             return valid;
33932         }
33933         
33934         if(valid){
33935             this.markValid();
33936             return valid;
33937         }
33938         
33939         this.markInvalid();
33940         
33941         return valid;
33942     },
33943     
33944     markValid : function()
33945     {
33946         
33947         var label = this.el.select('label', true).first();
33948         var icon = this.el.select('i.fa-star', true).first();
33949
33950         if(label && icon){
33951             icon.remove();
33952         }
33953         
33954         this.fireEvent('valid', this);
33955     },
33956     
33957      /**
33958      * Mark this field as invalid
33959      * @param {String} msg The validation message
33960      */
33961     markInvalid : function(msg)
33962     {
33963         
33964         var label = this.el.select('label', true).first();
33965         var icon = this.el.select('i.fa-star', true).first();
33966
33967         if(label && !icon){
33968             this.el.select('.roo-date-split-field-label', true).createChild({
33969                 tag : 'i',
33970                 cls : 'text-danger fa fa-lg fa-star',
33971                 tooltip : 'This field is required',
33972                 style : 'margin-right:5px;'
33973             }, label, true);
33974         }
33975         
33976         this.fireEvent('invalid', this, msg);
33977     },
33978     
33979     clearInvalid : function()
33980     {
33981         var label = this.el.select('label', true).first();
33982         var icon = this.el.select('i.fa-star', true).first();
33983
33984         if(label && icon){
33985             icon.remove();
33986         }
33987         
33988         this.fireEvent('valid', this);
33989     },
33990     
33991     getName: function()
33992     {
33993         return this.name;
33994     }
33995     
33996 });
33997
33998  
33999
34000 /**
34001  * @class Roo.bootstrap.LayoutMasonry
34002  * @extends Roo.bootstrap.Component
34003  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34004  * Bootstrap Layout Masonry class
34005  *
34006  * This is based on 
34007  * http://masonry.desandro.com
34008  *
34009  * The idea is to render all the bricks based on vertical width...
34010  *
34011  * The original code extends 'outlayer' - we might need to use that....
34012
34013  * @constructor
34014  * Create a new Element
34015  * @param {Object} config The config object
34016  */
34017
34018 Roo.bootstrap.LayoutMasonry = function(config){
34019     
34020     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34021     
34022     this.bricks = [];
34023     
34024     Roo.bootstrap.LayoutMasonry.register(this);
34025     
34026     this.addEvents({
34027         // raw events
34028         /**
34029          * @event layout
34030          * Fire after layout the items
34031          * @param {Roo.bootstrap.LayoutMasonry} this
34032          * @param {Roo.EventObject} e
34033          */
34034         "layout" : true
34035     });
34036     
34037 };
34038
34039 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34040     
34041     /**
34042      * @cfg {Boolean} isLayoutInstant = no animation?
34043      */   
34044     isLayoutInstant : false, // needed?
34045    
34046     /**
34047      * @cfg {Number} boxWidth  width of the columns
34048      */   
34049     boxWidth : 450,
34050     
34051       /**
34052      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34053      */   
34054     boxHeight : 0,
34055     
34056     /**
34057      * @cfg {Number} padWidth padding below box..
34058      */   
34059     padWidth : 10, 
34060     
34061     /**
34062      * @cfg {Number} gutter gutter width..
34063      */   
34064     gutter : 10,
34065     
34066      /**
34067      * @cfg {Number} maxCols maximum number of columns
34068      */   
34069     
34070     maxCols: 0,
34071     
34072     /**
34073      * @cfg {Boolean} isAutoInitial defalut true
34074      */   
34075     isAutoInitial : true, 
34076     
34077     containerWidth: 0,
34078     
34079     /**
34080      * @cfg {Boolean} isHorizontal defalut false
34081      */   
34082     isHorizontal : false, 
34083
34084     currentSize : null,
34085     
34086     tag: 'div',
34087     
34088     cls: '',
34089     
34090     bricks: null, //CompositeElement
34091     
34092     cols : 1,
34093     
34094     _isLayoutInited : false,
34095     
34096 //    isAlternative : false, // only use for vertical layout...
34097     
34098     /**
34099      * @cfg {Number} alternativePadWidth padding below box..
34100      */   
34101     alternativePadWidth : 50,
34102     
34103     selectedBrick : [],
34104     
34105     getAutoCreate : function(){
34106         
34107         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34108         
34109         var cfg = {
34110             tag: this.tag,
34111             cls: 'blog-masonary-wrapper ' + this.cls,
34112             cn : {
34113                 cls : 'mas-boxes masonary'
34114             }
34115         };
34116         
34117         return cfg;
34118     },
34119     
34120     getChildContainer: function( )
34121     {
34122         if (this.boxesEl) {
34123             return this.boxesEl;
34124         }
34125         
34126         this.boxesEl = this.el.select('.mas-boxes').first();
34127         
34128         return this.boxesEl;
34129     },
34130     
34131     
34132     initEvents : function()
34133     {
34134         var _this = this;
34135         
34136         if(this.isAutoInitial){
34137             Roo.log('hook children rendered');
34138             this.on('childrenrendered', function() {
34139                 Roo.log('children rendered');
34140                 _this.initial();
34141             } ,this);
34142         }
34143     },
34144     
34145     initial : function()
34146     {
34147         this.selectedBrick = [];
34148         
34149         this.currentSize = this.el.getBox(true);
34150         
34151         Roo.EventManager.onWindowResize(this.resize, this); 
34152
34153         if(!this.isAutoInitial){
34154             this.layout();
34155             return;
34156         }
34157         
34158         this.layout();
34159         
34160         return;
34161         //this.layout.defer(500,this);
34162         
34163     },
34164     
34165     resize : function()
34166     {
34167         var cs = this.el.getBox(true);
34168         
34169         if (
34170                 this.currentSize.width == cs.width && 
34171                 this.currentSize.x == cs.x && 
34172                 this.currentSize.height == cs.height && 
34173                 this.currentSize.y == cs.y 
34174         ) {
34175             Roo.log("no change in with or X or Y");
34176             return;
34177         }
34178         
34179         this.currentSize = cs;
34180         
34181         this.layout();
34182         
34183     },
34184     
34185     layout : function()
34186     {   
34187         this._resetLayout();
34188         
34189         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34190         
34191         this.layoutItems( isInstant );
34192       
34193         this._isLayoutInited = true;
34194         
34195         this.fireEvent('layout', this);
34196         
34197     },
34198     
34199     _resetLayout : function()
34200     {
34201         if(this.isHorizontal){
34202             this.horizontalMeasureColumns();
34203             return;
34204         }
34205         
34206         this.verticalMeasureColumns();
34207         
34208     },
34209     
34210     verticalMeasureColumns : function()
34211     {
34212         this.getContainerWidth();
34213         
34214 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34215 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34216 //            return;
34217 //        }
34218         
34219         var boxWidth = this.boxWidth + this.padWidth;
34220         
34221         if(this.containerWidth < this.boxWidth){
34222             boxWidth = this.containerWidth
34223         }
34224         
34225         var containerWidth = this.containerWidth;
34226         
34227         var cols = Math.floor(containerWidth / boxWidth);
34228         
34229         this.cols = Math.max( cols, 1 );
34230         
34231         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34232         
34233         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34234         
34235         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34236         
34237         this.colWidth = boxWidth + avail - this.padWidth;
34238         
34239         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34240         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34241     },
34242     
34243     horizontalMeasureColumns : function()
34244     {
34245         this.getContainerWidth();
34246         
34247         var boxWidth = this.boxWidth;
34248         
34249         if(this.containerWidth < boxWidth){
34250             boxWidth = this.containerWidth;
34251         }
34252         
34253         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34254         
34255         this.el.setHeight(boxWidth);
34256         
34257     },
34258     
34259     getContainerWidth : function()
34260     {
34261         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34262     },
34263     
34264     layoutItems : function( isInstant )
34265     {
34266         Roo.log(this.bricks);
34267         
34268         var items = Roo.apply([], this.bricks);
34269         
34270         if(this.isHorizontal){
34271             this._horizontalLayoutItems( items , isInstant );
34272             return;
34273         }
34274         
34275 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34276 //            this._verticalAlternativeLayoutItems( items , isInstant );
34277 //            return;
34278 //        }
34279         
34280         this._verticalLayoutItems( items , isInstant );
34281         
34282     },
34283     
34284     _verticalLayoutItems : function ( items , isInstant)
34285     {
34286         if ( !items || !items.length ) {
34287             return;
34288         }
34289         
34290         var standard = [
34291             ['xs', 'xs', 'xs', 'tall'],
34292             ['xs', 'xs', 'tall'],
34293             ['xs', 'xs', 'sm'],
34294             ['xs', 'xs', 'xs'],
34295             ['xs', 'tall'],
34296             ['xs', 'sm'],
34297             ['xs', 'xs'],
34298             ['xs'],
34299             
34300             ['sm', 'xs', 'xs'],
34301             ['sm', 'xs'],
34302             ['sm'],
34303             
34304             ['tall', 'xs', 'xs', 'xs'],
34305             ['tall', 'xs', 'xs'],
34306             ['tall', 'xs'],
34307             ['tall']
34308             
34309         ];
34310         
34311         var queue = [];
34312         
34313         var boxes = [];
34314         
34315         var box = [];
34316         
34317         Roo.each(items, function(item, k){
34318             
34319             switch (item.size) {
34320                 // these layouts take up a full box,
34321                 case 'md' :
34322                 case 'md-left' :
34323                 case 'md-right' :
34324                 case 'wide' :
34325                     
34326                     if(box.length){
34327                         boxes.push(box);
34328                         box = [];
34329                     }
34330                     
34331                     boxes.push([item]);
34332                     
34333                     break;
34334                     
34335                 case 'xs' :
34336                 case 'sm' :
34337                 case 'tall' :
34338                     
34339                     box.push(item);
34340                     
34341                     break;
34342                 default :
34343                     break;
34344                     
34345             }
34346             
34347         }, this);
34348         
34349         if(box.length){
34350             boxes.push(box);
34351             box = [];
34352         }
34353         
34354         var filterPattern = function(box, length)
34355         {
34356             if(!box.length){
34357                 return;
34358             }
34359             
34360             var match = false;
34361             
34362             var pattern = box.slice(0, length);
34363             
34364             var format = [];
34365             
34366             Roo.each(pattern, function(i){
34367                 format.push(i.size);
34368             }, this);
34369             
34370             Roo.each(standard, function(s){
34371                 
34372                 if(String(s) != String(format)){
34373                     return;
34374                 }
34375                 
34376                 match = true;
34377                 return false;
34378                 
34379             }, this);
34380             
34381             if(!match && length == 1){
34382                 return;
34383             }
34384             
34385             if(!match){
34386                 filterPattern(box, length - 1);
34387                 return;
34388             }
34389                 
34390             queue.push(pattern);
34391
34392             box = box.slice(length, box.length);
34393
34394             filterPattern(box, 4);
34395
34396             return;
34397             
34398         }
34399         
34400         Roo.each(boxes, function(box, k){
34401             
34402             if(!box.length){
34403                 return;
34404             }
34405             
34406             if(box.length == 1){
34407                 queue.push(box);
34408                 return;
34409             }
34410             
34411             filterPattern(box, 4);
34412             
34413         }, this);
34414         
34415         this._processVerticalLayoutQueue( queue, isInstant );
34416         
34417     },
34418     
34419 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34420 //    {
34421 //        if ( !items || !items.length ) {
34422 //            return;
34423 //        }
34424 //
34425 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34426 //        
34427 //    },
34428     
34429     _horizontalLayoutItems : function ( items , isInstant)
34430     {
34431         if ( !items || !items.length || items.length < 3) {
34432             return;
34433         }
34434         
34435         items.reverse();
34436         
34437         var eItems = items.slice(0, 3);
34438         
34439         items = items.slice(3, items.length);
34440         
34441         var standard = [
34442             ['xs', 'xs', 'xs', 'wide'],
34443             ['xs', 'xs', 'wide'],
34444             ['xs', 'xs', 'sm'],
34445             ['xs', 'xs', 'xs'],
34446             ['xs', 'wide'],
34447             ['xs', 'sm'],
34448             ['xs', 'xs'],
34449             ['xs'],
34450             
34451             ['sm', 'xs', 'xs'],
34452             ['sm', 'xs'],
34453             ['sm'],
34454             
34455             ['wide', 'xs', 'xs', 'xs'],
34456             ['wide', 'xs', 'xs'],
34457             ['wide', 'xs'],
34458             ['wide'],
34459             
34460             ['wide-thin']
34461         ];
34462         
34463         var queue = [];
34464         
34465         var boxes = [];
34466         
34467         var box = [];
34468         
34469         Roo.each(items, function(item, k){
34470             
34471             switch (item.size) {
34472                 case 'md' :
34473                 case 'md-left' :
34474                 case 'md-right' :
34475                 case 'tall' :
34476                     
34477                     if(box.length){
34478                         boxes.push(box);
34479                         box = [];
34480                     }
34481                     
34482                     boxes.push([item]);
34483                     
34484                     break;
34485                     
34486                 case 'xs' :
34487                 case 'sm' :
34488                 case 'wide' :
34489                 case 'wide-thin' :
34490                     
34491                     box.push(item);
34492                     
34493                     break;
34494                 default :
34495                     break;
34496                     
34497             }
34498             
34499         }, this);
34500         
34501         if(box.length){
34502             boxes.push(box);
34503             box = [];
34504         }
34505         
34506         var filterPattern = function(box, length)
34507         {
34508             if(!box.length){
34509                 return;
34510             }
34511             
34512             var match = false;
34513             
34514             var pattern = box.slice(0, length);
34515             
34516             var format = [];
34517             
34518             Roo.each(pattern, function(i){
34519                 format.push(i.size);
34520             }, this);
34521             
34522             Roo.each(standard, function(s){
34523                 
34524                 if(String(s) != String(format)){
34525                     return;
34526                 }
34527                 
34528                 match = true;
34529                 return false;
34530                 
34531             }, this);
34532             
34533             if(!match && length == 1){
34534                 return;
34535             }
34536             
34537             if(!match){
34538                 filterPattern(box, length - 1);
34539                 return;
34540             }
34541                 
34542             queue.push(pattern);
34543
34544             box = box.slice(length, box.length);
34545
34546             filterPattern(box, 4);
34547
34548             return;
34549             
34550         }
34551         
34552         Roo.each(boxes, function(box, k){
34553             
34554             if(!box.length){
34555                 return;
34556             }
34557             
34558             if(box.length == 1){
34559                 queue.push(box);
34560                 return;
34561             }
34562             
34563             filterPattern(box, 4);
34564             
34565         }, this);
34566         
34567         
34568         var prune = [];
34569         
34570         var pos = this.el.getBox(true);
34571         
34572         var minX = pos.x;
34573         
34574         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34575         
34576         var hit_end = false;
34577         
34578         Roo.each(queue, function(box){
34579             
34580             if(hit_end){
34581                 
34582                 Roo.each(box, function(b){
34583                 
34584                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34585                     b.el.hide();
34586
34587                 }, this);
34588
34589                 return;
34590             }
34591             
34592             var mx = 0;
34593             
34594             Roo.each(box, function(b){
34595                 
34596                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34597                 b.el.show();
34598
34599                 mx = Math.max(mx, b.x);
34600                 
34601             }, this);
34602             
34603             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34604             
34605             if(maxX < minX){
34606                 
34607                 Roo.each(box, function(b){
34608                 
34609                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34610                     b.el.hide();
34611                     
34612                 }, this);
34613                 
34614                 hit_end = true;
34615                 
34616                 return;
34617             }
34618             
34619             prune.push(box);
34620             
34621         }, this);
34622         
34623         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34624     },
34625     
34626     /** Sets position of item in DOM
34627     * @param {Element} item
34628     * @param {Number} x - horizontal position
34629     * @param {Number} y - vertical position
34630     * @param {Boolean} isInstant - disables transitions
34631     */
34632     _processVerticalLayoutQueue : function( queue, isInstant )
34633     {
34634         var pos = this.el.getBox(true);
34635         var x = pos.x;
34636         var y = pos.y;
34637         var maxY = [];
34638         
34639         for (var i = 0; i < this.cols; i++){
34640             maxY[i] = pos.y;
34641         }
34642         
34643         Roo.each(queue, function(box, k){
34644             
34645             var col = k % this.cols;
34646             
34647             Roo.each(box, function(b,kk){
34648                 
34649                 b.el.position('absolute');
34650                 
34651                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34652                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34653                 
34654                 if(b.size == 'md-left' || b.size == 'md-right'){
34655                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34656                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34657                 }
34658                 
34659                 b.el.setWidth(width);
34660                 b.el.setHeight(height);
34661                 // iframe?
34662                 b.el.select('iframe',true).setSize(width,height);
34663                 
34664             }, this);
34665             
34666             for (var i = 0; i < this.cols; i++){
34667                 
34668                 if(maxY[i] < maxY[col]){
34669                     col = i;
34670                     continue;
34671                 }
34672                 
34673                 col = Math.min(col, i);
34674                 
34675             }
34676             
34677             x = pos.x + col * (this.colWidth + this.padWidth);
34678             
34679             y = maxY[col];
34680             
34681             var positions = [];
34682             
34683             switch (box.length){
34684                 case 1 :
34685                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34686                     break;
34687                 case 2 :
34688                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34689                     break;
34690                 case 3 :
34691                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34692                     break;
34693                 case 4 :
34694                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34695                     break;
34696                 default :
34697                     break;
34698             }
34699             
34700             Roo.each(box, function(b,kk){
34701                 
34702                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34703                 
34704                 var sz = b.el.getSize();
34705                 
34706                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34707                 
34708             }, this);
34709             
34710         }, this);
34711         
34712         var mY = 0;
34713         
34714         for (var i = 0; i < this.cols; i++){
34715             mY = Math.max(mY, maxY[i]);
34716         }
34717         
34718         this.el.setHeight(mY - pos.y);
34719         
34720     },
34721     
34722 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34723 //    {
34724 //        var pos = this.el.getBox(true);
34725 //        var x = pos.x;
34726 //        var y = pos.y;
34727 //        var maxX = pos.right;
34728 //        
34729 //        var maxHeight = 0;
34730 //        
34731 //        Roo.each(items, function(item, k){
34732 //            
34733 //            var c = k % 2;
34734 //            
34735 //            item.el.position('absolute');
34736 //                
34737 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34738 //
34739 //            item.el.setWidth(width);
34740 //
34741 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34742 //
34743 //            item.el.setHeight(height);
34744 //            
34745 //            if(c == 0){
34746 //                item.el.setXY([x, y], isInstant ? false : true);
34747 //            } else {
34748 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34749 //            }
34750 //            
34751 //            y = y + height + this.alternativePadWidth;
34752 //            
34753 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34754 //            
34755 //        }, this);
34756 //        
34757 //        this.el.setHeight(maxHeight);
34758 //        
34759 //    },
34760     
34761     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34762     {
34763         var pos = this.el.getBox(true);
34764         
34765         var minX = pos.x;
34766         var minY = pos.y;
34767         
34768         var maxX = pos.right;
34769         
34770         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34771         
34772         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34773         
34774         Roo.each(queue, function(box, k){
34775             
34776             Roo.each(box, function(b, kk){
34777                 
34778                 b.el.position('absolute');
34779                 
34780                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34781                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34782                 
34783                 if(b.size == 'md-left' || b.size == 'md-right'){
34784                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34785                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34786                 }
34787                 
34788                 b.el.setWidth(width);
34789                 b.el.setHeight(height);
34790                 
34791             }, this);
34792             
34793             if(!box.length){
34794                 return;
34795             }
34796             
34797             var positions = [];
34798             
34799             switch (box.length){
34800                 case 1 :
34801                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34802                     break;
34803                 case 2 :
34804                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34805                     break;
34806                 case 3 :
34807                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34808                     break;
34809                 case 4 :
34810                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34811                     break;
34812                 default :
34813                     break;
34814             }
34815             
34816             Roo.each(box, function(b,kk){
34817                 
34818                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34819                 
34820                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34821                 
34822             }, this);
34823             
34824         }, this);
34825         
34826     },
34827     
34828     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34829     {
34830         Roo.each(eItems, function(b,k){
34831             
34832             b.size = (k == 0) ? 'sm' : 'xs';
34833             b.x = (k == 0) ? 2 : 1;
34834             b.y = (k == 0) ? 2 : 1;
34835             
34836             b.el.position('absolute');
34837             
34838             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34839                 
34840             b.el.setWidth(width);
34841             
34842             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34843             
34844             b.el.setHeight(height);
34845             
34846         }, this);
34847
34848         var positions = [];
34849         
34850         positions.push({
34851             x : maxX - this.unitWidth * 2 - this.gutter,
34852             y : minY
34853         });
34854         
34855         positions.push({
34856             x : maxX - this.unitWidth,
34857             y : minY + (this.unitWidth + this.gutter) * 2
34858         });
34859         
34860         positions.push({
34861             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34862             y : minY
34863         });
34864         
34865         Roo.each(eItems, function(b,k){
34866             
34867             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34868
34869         }, this);
34870         
34871     },
34872     
34873     getVerticalOneBoxColPositions : function(x, y, box)
34874     {
34875         var pos = [];
34876         
34877         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34878         
34879         if(box[0].size == 'md-left'){
34880             rand = 0;
34881         }
34882         
34883         if(box[0].size == 'md-right'){
34884             rand = 1;
34885         }
34886         
34887         pos.push({
34888             x : x + (this.unitWidth + this.gutter) * rand,
34889             y : y
34890         });
34891         
34892         return pos;
34893     },
34894     
34895     getVerticalTwoBoxColPositions : function(x, y, box)
34896     {
34897         var pos = [];
34898         
34899         if(box[0].size == 'xs'){
34900             
34901             pos.push({
34902                 x : x,
34903                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34904             });
34905
34906             pos.push({
34907                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34908                 y : y
34909             });
34910             
34911             return pos;
34912             
34913         }
34914         
34915         pos.push({
34916             x : x,
34917             y : y
34918         });
34919
34920         pos.push({
34921             x : x + (this.unitWidth + this.gutter) * 2,
34922             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34923         });
34924         
34925         return pos;
34926         
34927     },
34928     
34929     getVerticalThreeBoxColPositions : function(x, y, box)
34930     {
34931         var pos = [];
34932         
34933         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34934             
34935             pos.push({
34936                 x : x,
34937                 y : y
34938             });
34939
34940             pos.push({
34941                 x : x + (this.unitWidth + this.gutter) * 1,
34942                 y : y
34943             });
34944             
34945             pos.push({
34946                 x : x + (this.unitWidth + this.gutter) * 2,
34947                 y : y
34948             });
34949             
34950             return pos;
34951             
34952         }
34953         
34954         if(box[0].size == 'xs' && box[1].size == 'xs'){
34955             
34956             pos.push({
34957                 x : x,
34958                 y : y
34959             });
34960
34961             pos.push({
34962                 x : x,
34963                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34964             });
34965             
34966             pos.push({
34967                 x : x + (this.unitWidth + this.gutter) * 1,
34968                 y : y
34969             });
34970             
34971             return pos;
34972             
34973         }
34974         
34975         pos.push({
34976             x : x,
34977             y : y
34978         });
34979
34980         pos.push({
34981             x : x + (this.unitWidth + this.gutter) * 2,
34982             y : y
34983         });
34984
34985         pos.push({
34986             x : x + (this.unitWidth + this.gutter) * 2,
34987             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34988         });
34989             
34990         return pos;
34991         
34992     },
34993     
34994     getVerticalFourBoxColPositions : function(x, y, box)
34995     {
34996         var pos = [];
34997         
34998         if(box[0].size == 'xs'){
34999             
35000             pos.push({
35001                 x : x,
35002                 y : y
35003             });
35004
35005             pos.push({
35006                 x : x,
35007                 y : y + (this.unitHeight + this.gutter) * 1
35008             });
35009             
35010             pos.push({
35011                 x : x,
35012                 y : y + (this.unitHeight + this.gutter) * 2
35013             });
35014             
35015             pos.push({
35016                 x : x + (this.unitWidth + this.gutter) * 1,
35017                 y : y
35018             });
35019             
35020             return pos;
35021             
35022         }
35023         
35024         pos.push({
35025             x : x,
35026             y : y
35027         });
35028
35029         pos.push({
35030             x : x + (this.unitWidth + this.gutter) * 2,
35031             y : y
35032         });
35033
35034         pos.push({
35035             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35036             y : y + (this.unitHeight + this.gutter) * 1
35037         });
35038
35039         pos.push({
35040             x : x + (this.unitWidth + this.gutter) * 2,
35041             y : y + (this.unitWidth + this.gutter) * 2
35042         });
35043
35044         return pos;
35045         
35046     },
35047     
35048     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35049     {
35050         var pos = [];
35051         
35052         if(box[0].size == 'md-left'){
35053             pos.push({
35054                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35055                 y : minY
35056             });
35057             
35058             return pos;
35059         }
35060         
35061         if(box[0].size == 'md-right'){
35062             pos.push({
35063                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35064                 y : minY + (this.unitWidth + this.gutter) * 1
35065             });
35066             
35067             return pos;
35068         }
35069         
35070         var rand = Math.floor(Math.random() * (4 - box[0].y));
35071         
35072         pos.push({
35073             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35074             y : minY + (this.unitWidth + this.gutter) * rand
35075         });
35076         
35077         return pos;
35078         
35079     },
35080     
35081     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35082     {
35083         var pos = [];
35084         
35085         if(box[0].size == 'xs'){
35086             
35087             pos.push({
35088                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35089                 y : minY
35090             });
35091
35092             pos.push({
35093                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35094                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35095             });
35096             
35097             return pos;
35098             
35099         }
35100         
35101         pos.push({
35102             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35103             y : minY
35104         });
35105
35106         pos.push({
35107             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35108             y : minY + (this.unitWidth + this.gutter) * 2
35109         });
35110         
35111         return pos;
35112         
35113     },
35114     
35115     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35116     {
35117         var pos = [];
35118         
35119         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35120             
35121             pos.push({
35122                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35123                 y : minY
35124             });
35125
35126             pos.push({
35127                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35128                 y : minY + (this.unitWidth + this.gutter) * 1
35129             });
35130             
35131             pos.push({
35132                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35133                 y : minY + (this.unitWidth + this.gutter) * 2
35134             });
35135             
35136             return pos;
35137             
35138         }
35139         
35140         if(box[0].size == 'xs' && box[1].size == 'xs'){
35141             
35142             pos.push({
35143                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35144                 y : minY
35145             });
35146
35147             pos.push({
35148                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35149                 y : minY
35150             });
35151             
35152             pos.push({
35153                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35154                 y : minY + (this.unitWidth + this.gutter) * 1
35155             });
35156             
35157             return pos;
35158             
35159         }
35160         
35161         pos.push({
35162             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35163             y : minY
35164         });
35165
35166         pos.push({
35167             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35168             y : minY + (this.unitWidth + this.gutter) * 2
35169         });
35170
35171         pos.push({
35172             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35173             y : minY + (this.unitWidth + this.gutter) * 2
35174         });
35175             
35176         return pos;
35177         
35178     },
35179     
35180     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35181     {
35182         var pos = [];
35183         
35184         if(box[0].size == 'xs'){
35185             
35186             pos.push({
35187                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35188                 y : minY
35189             });
35190
35191             pos.push({
35192                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35193                 y : minY
35194             });
35195             
35196             pos.push({
35197                 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),
35198                 y : minY
35199             });
35200             
35201             pos.push({
35202                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35203                 y : minY + (this.unitWidth + this.gutter) * 1
35204             });
35205             
35206             return pos;
35207             
35208         }
35209         
35210         pos.push({
35211             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35212             y : minY
35213         });
35214         
35215         pos.push({
35216             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35217             y : minY + (this.unitWidth + this.gutter) * 2
35218         });
35219         
35220         pos.push({
35221             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35222             y : minY + (this.unitWidth + this.gutter) * 2
35223         });
35224         
35225         pos.push({
35226             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),
35227             y : minY + (this.unitWidth + this.gutter) * 2
35228         });
35229
35230         return pos;
35231         
35232     },
35233     
35234     /**
35235     * remove a Masonry Brick
35236     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35237     */
35238     removeBrick : function(brick_id)
35239     {
35240         if (!brick_id) {
35241             return;
35242         }
35243         
35244         for (var i = 0; i<this.bricks.length; i++) {
35245             if (this.bricks[i].id == brick_id) {
35246                 this.bricks.splice(i,1);
35247                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35248                 this.initial();
35249             }
35250         }
35251     },
35252     
35253     /**
35254     * adds a Masonry Brick
35255     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35256     */
35257     addBrick : function(cfg)
35258     {
35259         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35260         //this.register(cn);
35261         cn.parentId = this.id;
35262         cn.render(this.el);
35263         return cn;
35264     },
35265     
35266     /**
35267     * register a Masonry Brick
35268     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35269     */
35270     
35271     register : function(brick)
35272     {
35273         this.bricks.push(brick);
35274         brick.masonryId = this.id;
35275     },
35276     
35277     /**
35278     * clear all the Masonry Brick
35279     */
35280     clearAll : function()
35281     {
35282         this.bricks = [];
35283         //this.getChildContainer().dom.innerHTML = "";
35284         this.el.dom.innerHTML = '';
35285     },
35286     
35287     getSelected : function()
35288     {
35289         if (!this.selectedBrick) {
35290             return false;
35291         }
35292         
35293         return this.selectedBrick;
35294     }
35295 });
35296
35297 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35298     
35299     groups: {},
35300      /**
35301     * register a Masonry Layout
35302     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35303     */
35304     
35305     register : function(layout)
35306     {
35307         this.groups[layout.id] = layout;
35308     },
35309     /**
35310     * fetch a  Masonry Layout based on the masonry layout ID
35311     * @param {string} the masonry layout to add
35312     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35313     */
35314     
35315     get: function(layout_id) {
35316         if (typeof(this.groups[layout_id]) == 'undefined') {
35317             return false;
35318         }
35319         return this.groups[layout_id] ;
35320     }
35321     
35322     
35323     
35324 });
35325
35326  
35327
35328  /**
35329  *
35330  * This is based on 
35331  * http://masonry.desandro.com
35332  *
35333  * The idea is to render all the bricks based on vertical width...
35334  *
35335  * The original code extends 'outlayer' - we might need to use that....
35336  * 
35337  */
35338
35339
35340 /**
35341  * @class Roo.bootstrap.LayoutMasonryAuto
35342  * @extends Roo.bootstrap.Component
35343  * Bootstrap Layout Masonry class
35344  * 
35345  * @constructor
35346  * Create a new Element
35347  * @param {Object} config The config object
35348  */
35349
35350 Roo.bootstrap.LayoutMasonryAuto = function(config){
35351     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35352 };
35353
35354 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35355     
35356       /**
35357      * @cfg {Boolean} isFitWidth  - resize the width..
35358      */   
35359     isFitWidth : false,  // options..
35360     /**
35361      * @cfg {Boolean} isOriginLeft = left align?
35362      */   
35363     isOriginLeft : true,
35364     /**
35365      * @cfg {Boolean} isOriginTop = top align?
35366      */   
35367     isOriginTop : false,
35368     /**
35369      * @cfg {Boolean} isLayoutInstant = no animation?
35370      */   
35371     isLayoutInstant : false, // needed?
35372     /**
35373      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35374      */   
35375     isResizingContainer : true,
35376     /**
35377      * @cfg {Number} columnWidth  width of the columns 
35378      */   
35379     
35380     columnWidth : 0,
35381     
35382     /**
35383      * @cfg {Number} maxCols maximum number of columns
35384      */   
35385     
35386     maxCols: 0,
35387     /**
35388      * @cfg {Number} padHeight padding below box..
35389      */   
35390     
35391     padHeight : 10, 
35392     
35393     /**
35394      * @cfg {Boolean} isAutoInitial defalut true
35395      */   
35396     
35397     isAutoInitial : true, 
35398     
35399     // private?
35400     gutter : 0,
35401     
35402     containerWidth: 0,
35403     initialColumnWidth : 0,
35404     currentSize : null,
35405     
35406     colYs : null, // array.
35407     maxY : 0,
35408     padWidth: 10,
35409     
35410     
35411     tag: 'div',
35412     cls: '',
35413     bricks: null, //CompositeElement
35414     cols : 0, // array?
35415     // element : null, // wrapped now this.el
35416     _isLayoutInited : null, 
35417     
35418     
35419     getAutoCreate : function(){
35420         
35421         var cfg = {
35422             tag: this.tag,
35423             cls: 'blog-masonary-wrapper ' + this.cls,
35424             cn : {
35425                 cls : 'mas-boxes masonary'
35426             }
35427         };
35428         
35429         return cfg;
35430     },
35431     
35432     getChildContainer: function( )
35433     {
35434         if (this.boxesEl) {
35435             return this.boxesEl;
35436         }
35437         
35438         this.boxesEl = this.el.select('.mas-boxes').first();
35439         
35440         return this.boxesEl;
35441     },
35442     
35443     
35444     initEvents : function()
35445     {
35446         var _this = this;
35447         
35448         if(this.isAutoInitial){
35449             Roo.log('hook children rendered');
35450             this.on('childrenrendered', function() {
35451                 Roo.log('children rendered');
35452                 _this.initial();
35453             } ,this);
35454         }
35455         
35456     },
35457     
35458     initial : function()
35459     {
35460         this.reloadItems();
35461
35462         this.currentSize = this.el.getBox(true);
35463
35464         /// was window resize... - let's see if this works..
35465         Roo.EventManager.onWindowResize(this.resize, this); 
35466
35467         if(!this.isAutoInitial){
35468             this.layout();
35469             return;
35470         }
35471         
35472         this.layout.defer(500,this);
35473     },
35474     
35475     reloadItems: function()
35476     {
35477         this.bricks = this.el.select('.masonry-brick', true);
35478         
35479         this.bricks.each(function(b) {
35480             //Roo.log(b.getSize());
35481             if (!b.attr('originalwidth')) {
35482                 b.attr('originalwidth',  b.getSize().width);
35483             }
35484             
35485         });
35486         
35487         Roo.log(this.bricks.elements.length);
35488     },
35489     
35490     resize : function()
35491     {
35492         Roo.log('resize');
35493         var cs = this.el.getBox(true);
35494         
35495         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35496             Roo.log("no change in with or X");
35497             return;
35498         }
35499         this.currentSize = cs;
35500         this.layout();
35501     },
35502     
35503     layout : function()
35504     {
35505          Roo.log('layout');
35506         this._resetLayout();
35507         //this._manageStamps();
35508       
35509         // don't animate first layout
35510         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35511         this.layoutItems( isInstant );
35512       
35513         // flag for initalized
35514         this._isLayoutInited = true;
35515     },
35516     
35517     layoutItems : function( isInstant )
35518     {
35519         //var items = this._getItemsForLayout( this.items );
35520         // original code supports filtering layout items.. we just ignore it..
35521         
35522         this._layoutItems( this.bricks , isInstant );
35523       
35524         this._postLayout();
35525     },
35526     _layoutItems : function ( items , isInstant)
35527     {
35528        //this.fireEvent( 'layout', this, items );
35529     
35530
35531         if ( !items || !items.elements.length ) {
35532           // no items, emit event with empty array
35533             return;
35534         }
35535
35536         var queue = [];
35537         items.each(function(item) {
35538             Roo.log("layout item");
35539             Roo.log(item);
35540             // get x/y object from method
35541             var position = this._getItemLayoutPosition( item );
35542             // enqueue
35543             position.item = item;
35544             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35545             queue.push( position );
35546         }, this);
35547       
35548         this._processLayoutQueue( queue );
35549     },
35550     /** Sets position of item in DOM
35551     * @param {Element} item
35552     * @param {Number} x - horizontal position
35553     * @param {Number} y - vertical position
35554     * @param {Boolean} isInstant - disables transitions
35555     */
35556     _processLayoutQueue : function( queue )
35557     {
35558         for ( var i=0, len = queue.length; i < len; i++ ) {
35559             var obj = queue[i];
35560             obj.item.position('absolute');
35561             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35562         }
35563     },
35564       
35565     
35566     /**
35567     * Any logic you want to do after each layout,
35568     * i.e. size the container
35569     */
35570     _postLayout : function()
35571     {
35572         this.resizeContainer();
35573     },
35574     
35575     resizeContainer : function()
35576     {
35577         if ( !this.isResizingContainer ) {
35578             return;
35579         }
35580         var size = this._getContainerSize();
35581         if ( size ) {
35582             this.el.setSize(size.width,size.height);
35583             this.boxesEl.setSize(size.width,size.height);
35584         }
35585     },
35586     
35587     
35588     
35589     _resetLayout : function()
35590     {
35591         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35592         this.colWidth = this.el.getWidth();
35593         //this.gutter = this.el.getWidth(); 
35594         
35595         this.measureColumns();
35596
35597         // reset column Y
35598         var i = this.cols;
35599         this.colYs = [];
35600         while (i--) {
35601             this.colYs.push( 0 );
35602         }
35603     
35604         this.maxY = 0;
35605     },
35606
35607     measureColumns : function()
35608     {
35609         this.getContainerWidth();
35610       // if columnWidth is 0, default to outerWidth of first item
35611         if ( !this.columnWidth ) {
35612             var firstItem = this.bricks.first();
35613             Roo.log(firstItem);
35614             this.columnWidth  = this.containerWidth;
35615             if (firstItem && firstItem.attr('originalwidth') ) {
35616                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35617             }
35618             // columnWidth fall back to item of first element
35619             Roo.log("set column width?");
35620                         this.initialColumnWidth = this.columnWidth  ;
35621
35622             // if first elem has no width, default to size of container
35623             
35624         }
35625         
35626         
35627         if (this.initialColumnWidth) {
35628             this.columnWidth = this.initialColumnWidth;
35629         }
35630         
35631         
35632             
35633         // column width is fixed at the top - however if container width get's smaller we should
35634         // reduce it...
35635         
35636         // this bit calcs how man columns..
35637             
35638         var columnWidth = this.columnWidth += this.gutter;
35639       
35640         // calculate columns
35641         var containerWidth = this.containerWidth + this.gutter;
35642         
35643         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35644         // fix rounding errors, typically with gutters
35645         var excess = columnWidth - containerWidth % columnWidth;
35646         
35647         
35648         // if overshoot is less than a pixel, round up, otherwise floor it
35649         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35650         cols = Math[ mathMethod ]( cols );
35651         this.cols = Math.max( cols, 1 );
35652         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35653         
35654          // padding positioning..
35655         var totalColWidth = this.cols * this.columnWidth;
35656         var padavail = this.containerWidth - totalColWidth;
35657         // so for 2 columns - we need 3 'pads'
35658         
35659         var padNeeded = (1+this.cols) * this.padWidth;
35660         
35661         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35662         
35663         this.columnWidth += padExtra
35664         //this.padWidth = Math.floor(padavail /  ( this.cols));
35665         
35666         // adjust colum width so that padding is fixed??
35667         
35668         // we have 3 columns ... total = width * 3
35669         // we have X left over... that should be used by 
35670         
35671         //if (this.expandC) {
35672             
35673         //}
35674         
35675         
35676         
35677     },
35678     
35679     getContainerWidth : function()
35680     {
35681        /* // container is parent if fit width
35682         var container = this.isFitWidth ? this.element.parentNode : this.element;
35683         // check that this.size and size are there
35684         // IE8 triggers resize on body size change, so they might not be
35685         
35686         var size = getSize( container );  //FIXME
35687         this.containerWidth = size && size.innerWidth; //FIXME
35688         */
35689          
35690         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35691         
35692     },
35693     
35694     _getItemLayoutPosition : function( item )  // what is item?
35695     {
35696         // we resize the item to our columnWidth..
35697       
35698         item.setWidth(this.columnWidth);
35699         item.autoBoxAdjust  = false;
35700         
35701         var sz = item.getSize();
35702  
35703         // how many columns does this brick span
35704         var remainder = this.containerWidth % this.columnWidth;
35705         
35706         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35707         // round if off by 1 pixel, otherwise use ceil
35708         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35709         colSpan = Math.min( colSpan, this.cols );
35710         
35711         // normally this should be '1' as we dont' currently allow multi width columns..
35712         
35713         var colGroup = this._getColGroup( colSpan );
35714         // get the minimum Y value from the columns
35715         var minimumY = Math.min.apply( Math, colGroup );
35716         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35717         
35718         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35719          
35720         // position the brick
35721         var position = {
35722             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35723             y: this.currentSize.y + minimumY + this.padHeight
35724         };
35725         
35726         Roo.log(position);
35727         // apply setHeight to necessary columns
35728         var setHeight = minimumY + sz.height + this.padHeight;
35729         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35730         
35731         var setSpan = this.cols + 1 - colGroup.length;
35732         for ( var i = 0; i < setSpan; i++ ) {
35733           this.colYs[ shortColIndex + i ] = setHeight ;
35734         }
35735       
35736         return position;
35737     },
35738     
35739     /**
35740      * @param {Number} colSpan - number of columns the element spans
35741      * @returns {Array} colGroup
35742      */
35743     _getColGroup : function( colSpan )
35744     {
35745         if ( colSpan < 2 ) {
35746           // if brick spans only one column, use all the column Ys
35747           return this.colYs;
35748         }
35749       
35750         var colGroup = [];
35751         // how many different places could this brick fit horizontally
35752         var groupCount = this.cols + 1 - colSpan;
35753         // for each group potential horizontal position
35754         for ( var i = 0; i < groupCount; i++ ) {
35755           // make an array of colY values for that one group
35756           var groupColYs = this.colYs.slice( i, i + colSpan );
35757           // and get the max value of the array
35758           colGroup[i] = Math.max.apply( Math, groupColYs );
35759         }
35760         return colGroup;
35761     },
35762     /*
35763     _manageStamp : function( stamp )
35764     {
35765         var stampSize =  stamp.getSize();
35766         var offset = stamp.getBox();
35767         // get the columns that this stamp affects
35768         var firstX = this.isOriginLeft ? offset.x : offset.right;
35769         var lastX = firstX + stampSize.width;
35770         var firstCol = Math.floor( firstX / this.columnWidth );
35771         firstCol = Math.max( 0, firstCol );
35772         
35773         var lastCol = Math.floor( lastX / this.columnWidth );
35774         // lastCol should not go over if multiple of columnWidth #425
35775         lastCol -= lastX % this.columnWidth ? 0 : 1;
35776         lastCol = Math.min( this.cols - 1, lastCol );
35777         
35778         // set colYs to bottom of the stamp
35779         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35780             stampSize.height;
35781             
35782         for ( var i = firstCol; i <= lastCol; i++ ) {
35783           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35784         }
35785     },
35786     */
35787     
35788     _getContainerSize : function()
35789     {
35790         this.maxY = Math.max.apply( Math, this.colYs );
35791         var size = {
35792             height: this.maxY
35793         };
35794       
35795         if ( this.isFitWidth ) {
35796             size.width = this._getContainerFitWidth();
35797         }
35798       
35799         return size;
35800     },
35801     
35802     _getContainerFitWidth : function()
35803     {
35804         var unusedCols = 0;
35805         // count unused columns
35806         var i = this.cols;
35807         while ( --i ) {
35808           if ( this.colYs[i] !== 0 ) {
35809             break;
35810           }
35811           unusedCols++;
35812         }
35813         // fit container to columns that have been used
35814         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35815     },
35816     
35817     needsResizeLayout : function()
35818     {
35819         var previousWidth = this.containerWidth;
35820         this.getContainerWidth();
35821         return previousWidth !== this.containerWidth;
35822     }
35823  
35824 });
35825
35826  
35827
35828  /*
35829  * - LGPL
35830  *
35831  * element
35832  * 
35833  */
35834
35835 /**
35836  * @class Roo.bootstrap.MasonryBrick
35837  * @extends Roo.bootstrap.Component
35838  * Bootstrap MasonryBrick class
35839  * 
35840  * @constructor
35841  * Create a new MasonryBrick
35842  * @param {Object} config The config object
35843  */
35844
35845 Roo.bootstrap.MasonryBrick = function(config){
35846     
35847     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35848     
35849     Roo.bootstrap.MasonryBrick.register(this);
35850     
35851     this.addEvents({
35852         // raw events
35853         /**
35854          * @event click
35855          * When a MasonryBrick is clcik
35856          * @param {Roo.bootstrap.MasonryBrick} this
35857          * @param {Roo.EventObject} e
35858          */
35859         "click" : true
35860     });
35861 };
35862
35863 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35864     
35865     /**
35866      * @cfg {String} title
35867      */   
35868     title : '',
35869     /**
35870      * @cfg {String} html
35871      */   
35872     html : '',
35873     /**
35874      * @cfg {String} bgimage
35875      */   
35876     bgimage : '',
35877     /**
35878      * @cfg {String} videourl
35879      */   
35880     videourl : '',
35881     /**
35882      * @cfg {String} cls
35883      */   
35884     cls : '',
35885     /**
35886      * @cfg {String} href
35887      */   
35888     href : '',
35889     /**
35890      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35891      */   
35892     size : 'xs',
35893     
35894     /**
35895      * @cfg {String} placetitle (center|bottom)
35896      */   
35897     placetitle : '',
35898     
35899     /**
35900      * @cfg {Boolean} isFitContainer defalut true
35901      */   
35902     isFitContainer : true, 
35903     
35904     /**
35905      * @cfg {Boolean} preventDefault defalut false
35906      */   
35907     preventDefault : false, 
35908     
35909     /**
35910      * @cfg {Boolean} inverse defalut false
35911      */   
35912     maskInverse : false, 
35913     
35914     getAutoCreate : function()
35915     {
35916         if(!this.isFitContainer){
35917             return this.getSplitAutoCreate();
35918         }
35919         
35920         var cls = 'masonry-brick masonry-brick-full';
35921         
35922         if(this.href.length){
35923             cls += ' masonry-brick-link';
35924         }
35925         
35926         if(this.bgimage.length){
35927             cls += ' masonry-brick-image';
35928         }
35929         
35930         if(this.maskInverse){
35931             cls += ' mask-inverse';
35932         }
35933         
35934         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35935             cls += ' enable-mask';
35936         }
35937         
35938         if(this.size){
35939             cls += ' masonry-' + this.size + '-brick';
35940         }
35941         
35942         if(this.placetitle.length){
35943             
35944             switch (this.placetitle) {
35945                 case 'center' :
35946                     cls += ' masonry-center-title';
35947                     break;
35948                 case 'bottom' :
35949                     cls += ' masonry-bottom-title';
35950                     break;
35951                 default:
35952                     break;
35953             }
35954             
35955         } else {
35956             if(!this.html.length && !this.bgimage.length){
35957                 cls += ' masonry-center-title';
35958             }
35959
35960             if(!this.html.length && this.bgimage.length){
35961                 cls += ' masonry-bottom-title';
35962             }
35963         }
35964         
35965         if(this.cls){
35966             cls += ' ' + this.cls;
35967         }
35968         
35969         var cfg = {
35970             tag: (this.href.length) ? 'a' : 'div',
35971             cls: cls,
35972             cn: [
35973                 {
35974                     tag: 'div',
35975                     cls: 'masonry-brick-mask'
35976                 },
35977                 {
35978                     tag: 'div',
35979                     cls: 'masonry-brick-paragraph',
35980                     cn: []
35981                 }
35982             ]
35983         };
35984         
35985         if(this.href.length){
35986             cfg.href = this.href;
35987         }
35988         
35989         var cn = cfg.cn[1].cn;
35990         
35991         if(this.title.length){
35992             cn.push({
35993                 tag: 'h4',
35994                 cls: 'masonry-brick-title',
35995                 html: this.title
35996             });
35997         }
35998         
35999         if(this.html.length){
36000             cn.push({
36001                 tag: 'p',
36002                 cls: 'masonry-brick-text',
36003                 html: this.html
36004             });
36005         }
36006         
36007         if (!this.title.length && !this.html.length) {
36008             cfg.cn[1].cls += ' hide';
36009         }
36010         
36011         if(this.bgimage.length){
36012             cfg.cn.push({
36013                 tag: 'img',
36014                 cls: 'masonry-brick-image-view',
36015                 src: this.bgimage
36016             });
36017         }
36018         
36019         if(this.videourl.length){
36020             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36021             // youtube support only?
36022             cfg.cn.push({
36023                 tag: 'iframe',
36024                 cls: 'masonry-brick-image-view',
36025                 src: vurl,
36026                 frameborder : 0,
36027                 allowfullscreen : true
36028             });
36029         }
36030         
36031         return cfg;
36032         
36033     },
36034     
36035     getSplitAutoCreate : function()
36036     {
36037         var cls = 'masonry-brick masonry-brick-split';
36038         
36039         if(this.href.length){
36040             cls += ' masonry-brick-link';
36041         }
36042         
36043         if(this.bgimage.length){
36044             cls += ' masonry-brick-image';
36045         }
36046         
36047         if(this.size){
36048             cls += ' masonry-' + this.size + '-brick';
36049         }
36050         
36051         switch (this.placetitle) {
36052             case 'center' :
36053                 cls += ' masonry-center-title';
36054                 break;
36055             case 'bottom' :
36056                 cls += ' masonry-bottom-title';
36057                 break;
36058             default:
36059                 if(!this.bgimage.length){
36060                     cls += ' masonry-center-title';
36061                 }
36062
36063                 if(this.bgimage.length){
36064                     cls += ' masonry-bottom-title';
36065                 }
36066                 break;
36067         }
36068         
36069         if(this.cls){
36070             cls += ' ' + this.cls;
36071         }
36072         
36073         var cfg = {
36074             tag: (this.href.length) ? 'a' : 'div',
36075             cls: cls,
36076             cn: [
36077                 {
36078                     tag: 'div',
36079                     cls: 'masonry-brick-split-head',
36080                     cn: [
36081                         {
36082                             tag: 'div',
36083                             cls: 'masonry-brick-paragraph',
36084                             cn: []
36085                         }
36086                     ]
36087                 },
36088                 {
36089                     tag: 'div',
36090                     cls: 'masonry-brick-split-body',
36091                     cn: []
36092                 }
36093             ]
36094         };
36095         
36096         if(this.href.length){
36097             cfg.href = this.href;
36098         }
36099         
36100         if(this.title.length){
36101             cfg.cn[0].cn[0].cn.push({
36102                 tag: 'h4',
36103                 cls: 'masonry-brick-title',
36104                 html: this.title
36105             });
36106         }
36107         
36108         if(this.html.length){
36109             cfg.cn[1].cn.push({
36110                 tag: 'p',
36111                 cls: 'masonry-brick-text',
36112                 html: this.html
36113             });
36114         }
36115
36116         if(this.bgimage.length){
36117             cfg.cn[0].cn.push({
36118                 tag: 'img',
36119                 cls: 'masonry-brick-image-view',
36120                 src: this.bgimage
36121             });
36122         }
36123         
36124         if(this.videourl.length){
36125             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36126             // youtube support only?
36127             cfg.cn[0].cn.cn.push({
36128                 tag: 'iframe',
36129                 cls: 'masonry-brick-image-view',
36130                 src: vurl,
36131                 frameborder : 0,
36132                 allowfullscreen : true
36133             });
36134         }
36135         
36136         return cfg;
36137     },
36138     
36139     initEvents: function() 
36140     {
36141         switch (this.size) {
36142             case 'xs' :
36143                 this.x = 1;
36144                 this.y = 1;
36145                 break;
36146             case 'sm' :
36147                 this.x = 2;
36148                 this.y = 2;
36149                 break;
36150             case 'md' :
36151             case 'md-left' :
36152             case 'md-right' :
36153                 this.x = 3;
36154                 this.y = 3;
36155                 break;
36156             case 'tall' :
36157                 this.x = 2;
36158                 this.y = 3;
36159                 break;
36160             case 'wide' :
36161                 this.x = 3;
36162                 this.y = 2;
36163                 break;
36164             case 'wide-thin' :
36165                 this.x = 3;
36166                 this.y = 1;
36167                 break;
36168                         
36169             default :
36170                 break;
36171         }
36172         
36173         if(Roo.isTouch){
36174             this.el.on('touchstart', this.onTouchStart, this);
36175             this.el.on('touchmove', this.onTouchMove, this);
36176             this.el.on('touchend', this.onTouchEnd, this);
36177             this.el.on('contextmenu', this.onContextMenu, this);
36178         } else {
36179             this.el.on('mouseenter'  ,this.enter, this);
36180             this.el.on('mouseleave', this.leave, this);
36181             this.el.on('click', this.onClick, this);
36182         }
36183         
36184         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36185             this.parent().bricks.push(this);   
36186         }
36187         
36188     },
36189     
36190     onClick: function(e, el)
36191     {
36192         var time = this.endTimer - this.startTimer;
36193         // Roo.log(e.preventDefault());
36194         if(Roo.isTouch){
36195             if(time > 1000){
36196                 e.preventDefault();
36197                 return;
36198             }
36199         }
36200         
36201         if(!this.preventDefault){
36202             return;
36203         }
36204         
36205         e.preventDefault();
36206         
36207         if (this.activeClass != '') {
36208             this.selectBrick();
36209         }
36210         
36211         this.fireEvent('click', this, e);
36212     },
36213     
36214     enter: function(e, el)
36215     {
36216         e.preventDefault();
36217         
36218         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36219             return;
36220         }
36221         
36222         if(this.bgimage.length && this.html.length){
36223             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36224         }
36225     },
36226     
36227     leave: function(e, el)
36228     {
36229         e.preventDefault();
36230         
36231         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36232             return;
36233         }
36234         
36235         if(this.bgimage.length && this.html.length){
36236             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36237         }
36238     },
36239     
36240     onTouchStart: function(e, el)
36241     {
36242 //        e.preventDefault();
36243         
36244         this.touchmoved = false;
36245         
36246         if(!this.isFitContainer){
36247             return;
36248         }
36249         
36250         if(!this.bgimage.length || !this.html.length){
36251             return;
36252         }
36253         
36254         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36255         
36256         this.timer = new Date().getTime();
36257         
36258     },
36259     
36260     onTouchMove: function(e, el)
36261     {
36262         this.touchmoved = true;
36263     },
36264     
36265     onContextMenu : function(e,el)
36266     {
36267         e.preventDefault();
36268         e.stopPropagation();
36269         return false;
36270     },
36271     
36272     onTouchEnd: function(e, el)
36273     {
36274 //        e.preventDefault();
36275         
36276         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36277         
36278             this.leave(e,el);
36279             
36280             return;
36281         }
36282         
36283         if(!this.bgimage.length || !this.html.length){
36284             
36285             if(this.href.length){
36286                 window.location.href = this.href;
36287             }
36288             
36289             return;
36290         }
36291         
36292         if(!this.isFitContainer){
36293             return;
36294         }
36295         
36296         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36297         
36298         window.location.href = this.href;
36299     },
36300     
36301     //selection on single brick only
36302     selectBrick : function() {
36303         
36304         if (!this.parentId) {
36305             return;
36306         }
36307         
36308         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36309         var index = m.selectedBrick.indexOf(this.id);
36310         
36311         if ( index > -1) {
36312             m.selectedBrick.splice(index,1);
36313             this.el.removeClass(this.activeClass);
36314             return;
36315         }
36316         
36317         for(var i = 0; i < m.selectedBrick.length; i++) {
36318             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36319             b.el.removeClass(b.activeClass);
36320         }
36321         
36322         m.selectedBrick = [];
36323         
36324         m.selectedBrick.push(this.id);
36325         this.el.addClass(this.activeClass);
36326         return;
36327     },
36328     
36329     isSelected : function(){
36330         return this.el.hasClass(this.activeClass);
36331         
36332     }
36333 });
36334
36335 Roo.apply(Roo.bootstrap.MasonryBrick, {
36336     
36337     //groups: {},
36338     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36339      /**
36340     * register a Masonry Brick
36341     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36342     */
36343     
36344     register : function(brick)
36345     {
36346         //this.groups[brick.id] = brick;
36347         this.groups.add(brick.id, brick);
36348     },
36349     /**
36350     * fetch a  masonry brick based on the masonry brick ID
36351     * @param {string} the masonry brick to add
36352     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36353     */
36354     
36355     get: function(brick_id) 
36356     {
36357         // if (typeof(this.groups[brick_id]) == 'undefined') {
36358         //     return false;
36359         // }
36360         // return this.groups[brick_id] ;
36361         
36362         if(this.groups.key(brick_id)) {
36363             return this.groups.key(brick_id);
36364         }
36365         
36366         return false;
36367     }
36368     
36369     
36370     
36371 });
36372
36373  /*
36374  * - LGPL
36375  *
36376  * element
36377  * 
36378  */
36379
36380 /**
36381  * @class Roo.bootstrap.Brick
36382  * @extends Roo.bootstrap.Component
36383  * Bootstrap Brick class
36384  * 
36385  * @constructor
36386  * Create a new Brick
36387  * @param {Object} config The config object
36388  */
36389
36390 Roo.bootstrap.Brick = function(config){
36391     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36392     
36393     this.addEvents({
36394         // raw events
36395         /**
36396          * @event click
36397          * When a Brick is click
36398          * @param {Roo.bootstrap.Brick} this
36399          * @param {Roo.EventObject} e
36400          */
36401         "click" : true
36402     });
36403 };
36404
36405 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36406     
36407     /**
36408      * @cfg {String} title
36409      */   
36410     title : '',
36411     /**
36412      * @cfg {String} html
36413      */   
36414     html : '',
36415     /**
36416      * @cfg {String} bgimage
36417      */   
36418     bgimage : '',
36419     /**
36420      * @cfg {String} cls
36421      */   
36422     cls : '',
36423     /**
36424      * @cfg {String} href
36425      */   
36426     href : '',
36427     /**
36428      * @cfg {String} video
36429      */   
36430     video : '',
36431     /**
36432      * @cfg {Boolean} square
36433      */   
36434     square : true,
36435     
36436     getAutoCreate : function()
36437     {
36438         var cls = 'roo-brick';
36439         
36440         if(this.href.length){
36441             cls += ' roo-brick-link';
36442         }
36443         
36444         if(this.bgimage.length){
36445             cls += ' roo-brick-image';
36446         }
36447         
36448         if(!this.html.length && !this.bgimage.length){
36449             cls += ' roo-brick-center-title';
36450         }
36451         
36452         if(!this.html.length && this.bgimage.length){
36453             cls += ' roo-brick-bottom-title';
36454         }
36455         
36456         if(this.cls){
36457             cls += ' ' + this.cls;
36458         }
36459         
36460         var cfg = {
36461             tag: (this.href.length) ? 'a' : 'div',
36462             cls: cls,
36463             cn: [
36464                 {
36465                     tag: 'div',
36466                     cls: 'roo-brick-paragraph',
36467                     cn: []
36468                 }
36469             ]
36470         };
36471         
36472         if(this.href.length){
36473             cfg.href = this.href;
36474         }
36475         
36476         var cn = cfg.cn[0].cn;
36477         
36478         if(this.title.length){
36479             cn.push({
36480                 tag: 'h4',
36481                 cls: 'roo-brick-title',
36482                 html: this.title
36483             });
36484         }
36485         
36486         if(this.html.length){
36487             cn.push({
36488                 tag: 'p',
36489                 cls: 'roo-brick-text',
36490                 html: this.html
36491             });
36492         } else {
36493             cn.cls += ' hide';
36494         }
36495         
36496         if(this.bgimage.length){
36497             cfg.cn.push({
36498                 tag: 'img',
36499                 cls: 'roo-brick-image-view',
36500                 src: this.bgimage
36501             });
36502         }
36503         
36504         return cfg;
36505     },
36506     
36507     initEvents: function() 
36508     {
36509         if(this.title.length || this.html.length){
36510             this.el.on('mouseenter'  ,this.enter, this);
36511             this.el.on('mouseleave', this.leave, this);
36512         }
36513         
36514         Roo.EventManager.onWindowResize(this.resize, this); 
36515         
36516         if(this.bgimage.length){
36517             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36518             this.imageEl.on('load', this.onImageLoad, this);
36519             return;
36520         }
36521         
36522         this.resize();
36523     },
36524     
36525     onImageLoad : function()
36526     {
36527         this.resize();
36528     },
36529     
36530     resize : function()
36531     {
36532         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36533         
36534         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36535         
36536         if(this.bgimage.length){
36537             var image = this.el.select('.roo-brick-image-view', true).first();
36538             
36539             image.setWidth(paragraph.getWidth());
36540             
36541             if(this.square){
36542                 image.setHeight(paragraph.getWidth());
36543             }
36544             
36545             this.el.setHeight(image.getHeight());
36546             paragraph.setHeight(image.getHeight());
36547             
36548         }
36549         
36550     },
36551     
36552     enter: function(e, el)
36553     {
36554         e.preventDefault();
36555         
36556         if(this.bgimage.length){
36557             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36558             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36559         }
36560     },
36561     
36562     leave: function(e, el)
36563     {
36564         e.preventDefault();
36565         
36566         if(this.bgimage.length){
36567             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36568             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36569         }
36570     }
36571     
36572 });
36573
36574  
36575
36576  /*
36577  * - LGPL
36578  *
36579  * Number field 
36580  */
36581
36582 /**
36583  * @class Roo.bootstrap.NumberField
36584  * @extends Roo.bootstrap.Input
36585  * Bootstrap NumberField class
36586  * 
36587  * 
36588  * 
36589  * 
36590  * @constructor
36591  * Create a new NumberField
36592  * @param {Object} config The config object
36593  */
36594
36595 Roo.bootstrap.NumberField = function(config){
36596     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36597 };
36598
36599 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36600     
36601     /**
36602      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36603      */
36604     allowDecimals : true,
36605     /**
36606      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36607      */
36608     decimalSeparator : ".",
36609     /**
36610      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36611      */
36612     decimalPrecision : 2,
36613     /**
36614      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36615      */
36616     allowNegative : true,
36617     
36618     /**
36619      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36620      */
36621     allowZero: true,
36622     /**
36623      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36624      */
36625     minValue : Number.NEGATIVE_INFINITY,
36626     /**
36627      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36628      */
36629     maxValue : Number.MAX_VALUE,
36630     /**
36631      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36632      */
36633     minText : "The minimum value for this field is {0}",
36634     /**
36635      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36636      */
36637     maxText : "The maximum value for this field is {0}",
36638     /**
36639      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36640      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36641      */
36642     nanText : "{0} is not a valid number",
36643     /**
36644      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36645      */
36646     thousandsDelimiter : false,
36647     /**
36648      * @cfg {String} valueAlign alignment of value
36649      */
36650     valueAlign : "left",
36651
36652     getAutoCreate : function()
36653     {
36654         var hiddenInput = {
36655             tag: 'input',
36656             type: 'hidden',
36657             id: Roo.id(),
36658             cls: 'hidden-number-input'
36659         };
36660         
36661         if (this.name) {
36662             hiddenInput.name = this.name;
36663         }
36664         
36665         this.name = '';
36666         
36667         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36668         
36669         this.name = hiddenInput.name;
36670         
36671         if(cfg.cn.length > 0) {
36672             cfg.cn.push(hiddenInput);
36673         }
36674         
36675         return cfg;
36676     },
36677
36678     // private
36679     initEvents : function()
36680     {   
36681         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36682         
36683         var allowed = "0123456789";
36684         
36685         if(this.allowDecimals){
36686             allowed += this.decimalSeparator;
36687         }
36688         
36689         if(this.allowNegative){
36690             allowed += "-";
36691         }
36692         
36693         if(this.thousandsDelimiter) {
36694             allowed += ",";
36695         }
36696         
36697         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36698         
36699         var keyPress = function(e){
36700             
36701             var k = e.getKey();
36702             
36703             var c = e.getCharCode();
36704             
36705             if(
36706                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36707                     allowed.indexOf(String.fromCharCode(c)) === -1
36708             ){
36709                 e.stopEvent();
36710                 return;
36711             }
36712             
36713             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36714                 return;
36715             }
36716             
36717             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36718                 e.stopEvent();
36719             }
36720         };
36721         
36722         this.el.on("keypress", keyPress, this);
36723     },
36724     
36725     validateValue : function(value)
36726     {
36727         
36728         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36729             return false;
36730         }
36731         
36732         var num = this.parseValue(value);
36733         
36734         if(isNaN(num)){
36735             this.markInvalid(String.format(this.nanText, value));
36736             return false;
36737         }
36738         
36739         if(num < this.minValue){
36740             this.markInvalid(String.format(this.minText, this.minValue));
36741             return false;
36742         }
36743         
36744         if(num > this.maxValue){
36745             this.markInvalid(String.format(this.maxText, this.maxValue));
36746             return false;
36747         }
36748         
36749         return true;
36750     },
36751
36752     getValue : function()
36753     {
36754         var v = this.hiddenEl().getValue();
36755         
36756         return this.fixPrecision(this.parseValue(v));
36757     },
36758
36759     parseValue : function(value)
36760     {
36761         if(this.thousandsDelimiter) {
36762             value += "";
36763             r = new RegExp(",", "g");
36764             value = value.replace(r, "");
36765         }
36766         
36767         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36768         return isNaN(value) ? '' : value;
36769     },
36770
36771     fixPrecision : function(value)
36772     {
36773         if(this.thousandsDelimiter) {
36774             value += "";
36775             r = new RegExp(",", "g");
36776             value = value.replace(r, "");
36777         }
36778         
36779         var nan = isNaN(value);
36780         
36781         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36782             return nan ? '' : value;
36783         }
36784         return parseFloat(value).toFixed(this.decimalPrecision);
36785     },
36786
36787     setValue : function(v)
36788     {
36789         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36790         
36791         this.value = v;
36792         
36793         if(this.rendered){
36794             
36795             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36796             
36797             this.inputEl().dom.value = (v == '') ? '' :
36798                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36799             
36800             if(!this.allowZero && v === '0') {
36801                 this.hiddenEl().dom.value = '';
36802                 this.inputEl().dom.value = '';
36803             }
36804             
36805             this.validate();
36806         }
36807     },
36808
36809     decimalPrecisionFcn : function(v)
36810     {
36811         return Math.floor(v);
36812     },
36813
36814     beforeBlur : function()
36815     {
36816         var v = this.parseValue(this.getRawValue());
36817         
36818         if(v || v === 0 || v === ''){
36819             this.setValue(v);
36820         }
36821     },
36822     
36823     hiddenEl : function()
36824     {
36825         return this.el.select('input.hidden-number-input',true).first();
36826     }
36827     
36828 });
36829
36830  
36831
36832 /*
36833 * Licence: LGPL
36834 */
36835
36836 /**
36837  * @class Roo.bootstrap.DocumentSlider
36838  * @extends Roo.bootstrap.Component
36839  * Bootstrap DocumentSlider class
36840  * 
36841  * @constructor
36842  * Create a new DocumentViewer
36843  * @param {Object} config The config object
36844  */
36845
36846 Roo.bootstrap.DocumentSlider = function(config){
36847     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36848     
36849     this.files = [];
36850     
36851     this.addEvents({
36852         /**
36853          * @event initial
36854          * Fire after initEvent
36855          * @param {Roo.bootstrap.DocumentSlider} this
36856          */
36857         "initial" : true,
36858         /**
36859          * @event update
36860          * Fire after update
36861          * @param {Roo.bootstrap.DocumentSlider} this
36862          */
36863         "update" : true,
36864         /**
36865          * @event click
36866          * Fire after click
36867          * @param {Roo.bootstrap.DocumentSlider} this
36868          */
36869         "click" : true
36870     });
36871 };
36872
36873 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36874     
36875     files : false,
36876     
36877     indicator : 0,
36878     
36879     getAutoCreate : function()
36880     {
36881         var cfg = {
36882             tag : 'div',
36883             cls : 'roo-document-slider',
36884             cn : [
36885                 {
36886                     tag : 'div',
36887                     cls : 'roo-document-slider-header',
36888                     cn : [
36889                         {
36890                             tag : 'div',
36891                             cls : 'roo-document-slider-header-title'
36892                         }
36893                     ]
36894                 },
36895                 {
36896                     tag : 'div',
36897                     cls : 'roo-document-slider-body',
36898                     cn : [
36899                         {
36900                             tag : 'div',
36901                             cls : 'roo-document-slider-prev',
36902                             cn : [
36903                                 {
36904                                     tag : 'i',
36905                                     cls : 'fa fa-chevron-left'
36906                                 }
36907                             ]
36908                         },
36909                         {
36910                             tag : 'div',
36911                             cls : 'roo-document-slider-thumb',
36912                             cn : [
36913                                 {
36914                                     tag : 'img',
36915                                     cls : 'roo-document-slider-image'
36916                                 }
36917                             ]
36918                         },
36919                         {
36920                             tag : 'div',
36921                             cls : 'roo-document-slider-next',
36922                             cn : [
36923                                 {
36924                                     tag : 'i',
36925                                     cls : 'fa fa-chevron-right'
36926                                 }
36927                             ]
36928                         }
36929                     ]
36930                 }
36931             ]
36932         };
36933         
36934         return cfg;
36935     },
36936     
36937     initEvents : function()
36938     {
36939         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36940         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36941         
36942         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36943         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36944         
36945         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36946         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36947         
36948         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36949         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36950         
36951         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36952         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36953         
36954         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36955         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36956         
36957         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36958         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36959         
36960         this.thumbEl.on('click', this.onClick, this);
36961         
36962         this.prevIndicator.on('click', this.prev, this);
36963         
36964         this.nextIndicator.on('click', this.next, this);
36965         
36966     },
36967     
36968     initial : function()
36969     {
36970         if(this.files.length){
36971             this.indicator = 1;
36972             this.update()
36973         }
36974         
36975         this.fireEvent('initial', this);
36976     },
36977     
36978     update : function()
36979     {
36980         this.imageEl.attr('src', this.files[this.indicator - 1]);
36981         
36982         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36983         
36984         this.prevIndicator.show();
36985         
36986         if(this.indicator == 1){
36987             this.prevIndicator.hide();
36988         }
36989         
36990         this.nextIndicator.show();
36991         
36992         if(this.indicator == this.files.length){
36993             this.nextIndicator.hide();
36994         }
36995         
36996         this.thumbEl.scrollTo('top');
36997         
36998         this.fireEvent('update', this);
36999     },
37000     
37001     onClick : function(e)
37002     {
37003         e.preventDefault();
37004         
37005         this.fireEvent('click', this);
37006     },
37007     
37008     prev : function(e)
37009     {
37010         e.preventDefault();
37011         
37012         this.indicator = Math.max(1, this.indicator - 1);
37013         
37014         this.update();
37015     },
37016     
37017     next : function(e)
37018     {
37019         e.preventDefault();
37020         
37021         this.indicator = Math.min(this.files.length, this.indicator + 1);
37022         
37023         this.update();
37024     }
37025 });
37026 /*
37027  * - LGPL
37028  *
37029  * RadioSet
37030  *
37031  *
37032  */
37033
37034 /**
37035  * @class Roo.bootstrap.RadioSet
37036  * @extends Roo.bootstrap.Input
37037  * @children Roo.bootstrap.Radio
37038  * Bootstrap RadioSet class
37039  * @cfg {String} indicatorpos (left|right) default left
37040  * @cfg {Boolean} inline (true|false) inline the element (default true)
37041  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37042  * @constructor
37043  * Create a new RadioSet
37044  * @param {Object} config The config object
37045  */
37046
37047 Roo.bootstrap.RadioSet = function(config){
37048     
37049     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37050     
37051     this.radioes = [];
37052     
37053     Roo.bootstrap.RadioSet.register(this);
37054     
37055     this.addEvents({
37056         /**
37057         * @event check
37058         * Fires when the element is checked or unchecked.
37059         * @param {Roo.bootstrap.RadioSet} this This radio
37060         * @param {Roo.bootstrap.Radio} item The checked item
37061         */
37062        check : true,
37063        /**
37064         * @event click
37065         * Fires when the element is click.
37066         * @param {Roo.bootstrap.RadioSet} this This radio set
37067         * @param {Roo.bootstrap.Radio} item The checked item
37068         * @param {Roo.EventObject} e The event object
37069         */
37070        click : true
37071     });
37072     
37073 };
37074
37075 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37076
37077     radioes : false,
37078     
37079     inline : true,
37080     
37081     weight : '',
37082     
37083     indicatorpos : 'left',
37084     
37085     getAutoCreate : function()
37086     {
37087         var label = {
37088             tag : 'label',
37089             cls : 'roo-radio-set-label',
37090             cn : [
37091                 {
37092                     tag : 'span',
37093                     html : this.fieldLabel
37094                 }
37095             ]
37096         };
37097         if (Roo.bootstrap.version == 3) {
37098             
37099             
37100             if(this.indicatorpos == 'left'){
37101                 label.cn.unshift({
37102                     tag : 'i',
37103                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37104                     tooltip : 'This field is required'
37105                 });
37106             } else {
37107                 label.cn.push({
37108                     tag : 'i',
37109                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37110                     tooltip : 'This field is required'
37111                 });
37112             }
37113         }
37114         var items = {
37115             tag : 'div',
37116             cls : 'roo-radio-set-items'
37117         };
37118         
37119         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37120         
37121         if (align === 'left' && this.fieldLabel.length) {
37122             
37123             items = {
37124                 cls : "roo-radio-set-right", 
37125                 cn: [
37126                     items
37127                 ]
37128             };
37129             
37130             if(this.labelWidth > 12){
37131                 label.style = "width: " + this.labelWidth + 'px';
37132             }
37133             
37134             if(this.labelWidth < 13 && this.labelmd == 0){
37135                 this.labelmd = this.labelWidth;
37136             }
37137             
37138             if(this.labellg > 0){
37139                 label.cls += ' col-lg-' + this.labellg;
37140                 items.cls += ' col-lg-' + (12 - this.labellg);
37141             }
37142             
37143             if(this.labelmd > 0){
37144                 label.cls += ' col-md-' + this.labelmd;
37145                 items.cls += ' col-md-' + (12 - this.labelmd);
37146             }
37147             
37148             if(this.labelsm > 0){
37149                 label.cls += ' col-sm-' + this.labelsm;
37150                 items.cls += ' col-sm-' + (12 - this.labelsm);
37151             }
37152             
37153             if(this.labelxs > 0){
37154                 label.cls += ' col-xs-' + this.labelxs;
37155                 items.cls += ' col-xs-' + (12 - this.labelxs);
37156             }
37157         }
37158         
37159         var cfg = {
37160             tag : 'div',
37161             cls : 'roo-radio-set',
37162             cn : [
37163                 {
37164                     tag : 'input',
37165                     cls : 'roo-radio-set-input',
37166                     type : 'hidden',
37167                     name : this.name,
37168                     value : this.value ? this.value :  ''
37169                 },
37170                 label,
37171                 items
37172             ]
37173         };
37174         
37175         if(this.weight.length){
37176             cfg.cls += ' roo-radio-' + this.weight;
37177         }
37178         
37179         if(this.inline) {
37180             cfg.cls += ' roo-radio-set-inline';
37181         }
37182         
37183         var settings=this;
37184         ['xs','sm','md','lg'].map(function(size){
37185             if (settings[size]) {
37186                 cfg.cls += ' col-' + size + '-' + settings[size];
37187             }
37188         });
37189         
37190         return cfg;
37191         
37192     },
37193
37194     initEvents : function()
37195     {
37196         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37197         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37198         
37199         if(!this.fieldLabel.length){
37200             this.labelEl.hide();
37201         }
37202         
37203         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37204         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37205         
37206         this.indicator = this.indicatorEl();
37207         
37208         if(this.indicator){
37209             this.indicator.addClass('invisible');
37210         }
37211         
37212         this.originalValue = this.getValue();
37213         
37214     },
37215     
37216     inputEl: function ()
37217     {
37218         return this.el.select('.roo-radio-set-input', true).first();
37219     },
37220     
37221     getChildContainer : function()
37222     {
37223         return this.itemsEl;
37224     },
37225     
37226     register : function(item)
37227     {
37228         this.radioes.push(item);
37229         
37230     },
37231     
37232     validate : function()
37233     {   
37234         if(this.getVisibilityEl().hasClass('hidden')){
37235             return true;
37236         }
37237         
37238         var valid = false;
37239         
37240         Roo.each(this.radioes, function(i){
37241             if(!i.checked){
37242                 return;
37243             }
37244             
37245             valid = true;
37246             return false;
37247         });
37248         
37249         if(this.allowBlank) {
37250             return true;
37251         }
37252         
37253         if(this.disabled || valid){
37254             this.markValid();
37255             return true;
37256         }
37257         
37258         this.markInvalid();
37259         return false;
37260         
37261     },
37262     
37263     markValid : function()
37264     {
37265         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37266             this.indicatorEl().removeClass('visible');
37267             this.indicatorEl().addClass('invisible');
37268         }
37269         
37270         
37271         if (Roo.bootstrap.version == 3) {
37272             this.el.removeClass([this.invalidClass, this.validClass]);
37273             this.el.addClass(this.validClass);
37274         } else {
37275             this.el.removeClass(['is-invalid','is-valid']);
37276             this.el.addClass(['is-valid']);
37277         }
37278         this.fireEvent('valid', this);
37279     },
37280     
37281     markInvalid : function(msg)
37282     {
37283         if(this.allowBlank || this.disabled){
37284             return;
37285         }
37286         
37287         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37288             this.indicatorEl().removeClass('invisible');
37289             this.indicatorEl().addClass('visible');
37290         }
37291         if (Roo.bootstrap.version == 3) {
37292             this.el.removeClass([this.invalidClass, this.validClass]);
37293             this.el.addClass(this.invalidClass);
37294         } else {
37295             this.el.removeClass(['is-invalid','is-valid']);
37296             this.el.addClass(['is-invalid']);
37297         }
37298         
37299         this.fireEvent('invalid', this, msg);
37300         
37301     },
37302     
37303     setValue : function(v, suppressEvent)
37304     {   
37305         if(this.value === v){
37306             return;
37307         }
37308         
37309         this.value = v;
37310         
37311         if(this.rendered){
37312             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37313         }
37314         
37315         Roo.each(this.radioes, function(i){
37316             i.checked = false;
37317             i.el.removeClass('checked');
37318         });
37319         
37320         Roo.each(this.radioes, function(i){
37321             
37322             if(i.value === v || i.value.toString() === v.toString()){
37323                 i.checked = true;
37324                 i.el.addClass('checked');
37325                 
37326                 if(suppressEvent !== true){
37327                     this.fireEvent('check', this, i);
37328                 }
37329                 
37330                 return false;
37331             }
37332             
37333         }, this);
37334         
37335         this.validate();
37336     },
37337     
37338     clearInvalid : function(){
37339         
37340         if(!this.el || this.preventMark){
37341             return;
37342         }
37343         
37344         this.el.removeClass([this.invalidClass]);
37345         
37346         this.fireEvent('valid', this);
37347     }
37348     
37349 });
37350
37351 Roo.apply(Roo.bootstrap.RadioSet, {
37352     
37353     groups: {},
37354     
37355     register : function(set)
37356     {
37357         this.groups[set.name] = set;
37358     },
37359     
37360     get: function(name) 
37361     {
37362         if (typeof(this.groups[name]) == 'undefined') {
37363             return false;
37364         }
37365         
37366         return this.groups[name] ;
37367     }
37368     
37369 });
37370 /*
37371  * Based on:
37372  * Ext JS Library 1.1.1
37373  * Copyright(c) 2006-2007, Ext JS, LLC.
37374  *
37375  * Originally Released Under LGPL - original licence link has changed is not relivant.
37376  *
37377  * Fork - LGPL
37378  * <script type="text/javascript">
37379  */
37380
37381
37382 /**
37383  * @class Roo.bootstrap.SplitBar
37384  * @extends Roo.util.Observable
37385  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37386  * <br><br>
37387  * Usage:
37388  * <pre><code>
37389 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37390                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37391 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37392 split.minSize = 100;
37393 split.maxSize = 600;
37394 split.animate = true;
37395 split.on('moved', splitterMoved);
37396 </code></pre>
37397  * @constructor
37398  * Create a new SplitBar
37399  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37400  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37401  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37402  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37403                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37404                         position of the SplitBar).
37405  */
37406 Roo.bootstrap.SplitBar = function(cfg){
37407     
37408     /** @private */
37409     
37410     //{
37411     //  dragElement : elm
37412     //  resizingElement: el,
37413         // optional..
37414     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37415     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37416         // existingProxy ???
37417     //}
37418     
37419     this.el = Roo.get(cfg.dragElement, true);
37420     this.el.dom.unselectable = "on";
37421     /** @private */
37422     this.resizingEl = Roo.get(cfg.resizingElement, true);
37423
37424     /**
37425      * @private
37426      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37427      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37428      * @type Number
37429      */
37430     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37431     
37432     /**
37433      * The minimum size of the resizing element. (Defaults to 0)
37434      * @type Number
37435      */
37436     this.minSize = 0;
37437     
37438     /**
37439      * The maximum size of the resizing element. (Defaults to 2000)
37440      * @type Number
37441      */
37442     this.maxSize = 2000;
37443     
37444     /**
37445      * Whether to animate the transition to the new size
37446      * @type Boolean
37447      */
37448     this.animate = false;
37449     
37450     /**
37451      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37452      * @type Boolean
37453      */
37454     this.useShim = false;
37455     
37456     /** @private */
37457     this.shim = null;
37458     
37459     if(!cfg.existingProxy){
37460         /** @private */
37461         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37462     }else{
37463         this.proxy = Roo.get(cfg.existingProxy).dom;
37464     }
37465     /** @private */
37466     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37467     
37468     /** @private */
37469     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37470     
37471     /** @private */
37472     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37473     
37474     /** @private */
37475     this.dragSpecs = {};
37476     
37477     /**
37478      * @private The adapter to use to positon and resize elements
37479      */
37480     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37481     this.adapter.init(this);
37482     
37483     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37484         /** @private */
37485         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37486         this.el.addClass("roo-splitbar-h");
37487     }else{
37488         /** @private */
37489         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37490         this.el.addClass("roo-splitbar-v");
37491     }
37492     
37493     this.addEvents({
37494         /**
37495          * @event resize
37496          * Fires when the splitter is moved (alias for {@link #event-moved})
37497          * @param {Roo.bootstrap.SplitBar} this
37498          * @param {Number} newSize the new width or height
37499          */
37500         "resize" : true,
37501         /**
37502          * @event moved
37503          * Fires when the splitter is moved
37504          * @param {Roo.bootstrap.SplitBar} this
37505          * @param {Number} newSize the new width or height
37506          */
37507         "moved" : true,
37508         /**
37509          * @event beforeresize
37510          * Fires before the splitter is dragged
37511          * @param {Roo.bootstrap.SplitBar} this
37512          */
37513         "beforeresize" : true,
37514
37515         "beforeapply" : true
37516     });
37517
37518     Roo.util.Observable.call(this);
37519 };
37520
37521 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37522     onStartProxyDrag : function(x, y){
37523         this.fireEvent("beforeresize", this);
37524         if(!this.overlay){
37525             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37526             o.unselectable();
37527             o.enableDisplayMode("block");
37528             // all splitbars share the same overlay
37529             Roo.bootstrap.SplitBar.prototype.overlay = o;
37530         }
37531         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37532         this.overlay.show();
37533         Roo.get(this.proxy).setDisplayed("block");
37534         var size = this.adapter.getElementSize(this);
37535         this.activeMinSize = this.getMinimumSize();;
37536         this.activeMaxSize = this.getMaximumSize();;
37537         var c1 = size - this.activeMinSize;
37538         var c2 = Math.max(this.activeMaxSize - size, 0);
37539         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37540             this.dd.resetConstraints();
37541             this.dd.setXConstraint(
37542                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37543                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37544             );
37545             this.dd.setYConstraint(0, 0);
37546         }else{
37547             this.dd.resetConstraints();
37548             this.dd.setXConstraint(0, 0);
37549             this.dd.setYConstraint(
37550                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37551                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37552             );
37553          }
37554         this.dragSpecs.startSize = size;
37555         this.dragSpecs.startPoint = [x, y];
37556         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37557     },
37558     
37559     /** 
37560      * @private Called after the drag operation by the DDProxy
37561      */
37562     onEndProxyDrag : function(e){
37563         Roo.get(this.proxy).setDisplayed(false);
37564         var endPoint = Roo.lib.Event.getXY(e);
37565         if(this.overlay){
37566             this.overlay.hide();
37567         }
37568         var newSize;
37569         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37570             newSize = this.dragSpecs.startSize + 
37571                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37572                     endPoint[0] - this.dragSpecs.startPoint[0] :
37573                     this.dragSpecs.startPoint[0] - endPoint[0]
37574                 );
37575         }else{
37576             newSize = this.dragSpecs.startSize + 
37577                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37578                     endPoint[1] - this.dragSpecs.startPoint[1] :
37579                     this.dragSpecs.startPoint[1] - endPoint[1]
37580                 );
37581         }
37582         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37583         if(newSize != this.dragSpecs.startSize){
37584             if(this.fireEvent('beforeapply', this, newSize) !== false){
37585                 this.adapter.setElementSize(this, newSize);
37586                 this.fireEvent("moved", this, newSize);
37587                 this.fireEvent("resize", this, newSize);
37588             }
37589         }
37590     },
37591     
37592     /**
37593      * Get the adapter this SplitBar uses
37594      * @return The adapter object
37595      */
37596     getAdapter : function(){
37597         return this.adapter;
37598     },
37599     
37600     /**
37601      * Set the adapter this SplitBar uses
37602      * @param {Object} adapter A SplitBar adapter object
37603      */
37604     setAdapter : function(adapter){
37605         this.adapter = adapter;
37606         this.adapter.init(this);
37607     },
37608     
37609     /**
37610      * Gets the minimum size for the resizing element
37611      * @return {Number} The minimum size
37612      */
37613     getMinimumSize : function(){
37614         return this.minSize;
37615     },
37616     
37617     /**
37618      * Sets the minimum size for the resizing element
37619      * @param {Number} minSize The minimum size
37620      */
37621     setMinimumSize : function(minSize){
37622         this.minSize = minSize;
37623     },
37624     
37625     /**
37626      * Gets the maximum size for the resizing element
37627      * @return {Number} The maximum size
37628      */
37629     getMaximumSize : function(){
37630         return this.maxSize;
37631     },
37632     
37633     /**
37634      * Sets the maximum size for the resizing element
37635      * @param {Number} maxSize The maximum size
37636      */
37637     setMaximumSize : function(maxSize){
37638         this.maxSize = maxSize;
37639     },
37640     
37641     /**
37642      * Sets the initialize size for the resizing element
37643      * @param {Number} size The initial size
37644      */
37645     setCurrentSize : function(size){
37646         var oldAnimate = this.animate;
37647         this.animate = false;
37648         this.adapter.setElementSize(this, size);
37649         this.animate = oldAnimate;
37650     },
37651     
37652     /**
37653      * Destroy this splitbar. 
37654      * @param {Boolean} removeEl True to remove the element
37655      */
37656     destroy : function(removeEl){
37657         if(this.shim){
37658             this.shim.remove();
37659         }
37660         this.dd.unreg();
37661         this.proxy.parentNode.removeChild(this.proxy);
37662         if(removeEl){
37663             this.el.remove();
37664         }
37665     }
37666 });
37667
37668 /**
37669  * @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.
37670  */
37671 Roo.bootstrap.SplitBar.createProxy = function(dir){
37672     var proxy = new Roo.Element(document.createElement("div"));
37673     proxy.unselectable();
37674     var cls = 'roo-splitbar-proxy';
37675     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37676     document.body.appendChild(proxy.dom);
37677     return proxy.dom;
37678 };
37679
37680 /** 
37681  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37682  * Default Adapter. It assumes the splitter and resizing element are not positioned
37683  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37684  */
37685 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37686 };
37687
37688 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37689     // do nothing for now
37690     init : function(s){
37691     
37692     },
37693     /**
37694      * Called before drag operations to get the current size of the resizing element. 
37695      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37696      */
37697      getElementSize : function(s){
37698         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37699             return s.resizingEl.getWidth();
37700         }else{
37701             return s.resizingEl.getHeight();
37702         }
37703     },
37704     
37705     /**
37706      * Called after drag operations to set the size of the resizing element.
37707      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37708      * @param {Number} newSize The new size to set
37709      * @param {Function} onComplete A function to be invoked when resizing is complete
37710      */
37711     setElementSize : function(s, newSize, onComplete){
37712         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37713             if(!s.animate){
37714                 s.resizingEl.setWidth(newSize);
37715                 if(onComplete){
37716                     onComplete(s, newSize);
37717                 }
37718             }else{
37719                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37720             }
37721         }else{
37722             
37723             if(!s.animate){
37724                 s.resizingEl.setHeight(newSize);
37725                 if(onComplete){
37726                     onComplete(s, newSize);
37727                 }
37728             }else{
37729                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37730             }
37731         }
37732     }
37733 };
37734
37735 /** 
37736  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37737  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37738  * Adapter that  moves the splitter element to align with the resized sizing element. 
37739  * Used with an absolute positioned SplitBar.
37740  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37741  * document.body, make sure you assign an id to the body element.
37742  */
37743 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37744     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37745     this.container = Roo.get(container);
37746 };
37747
37748 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37749     init : function(s){
37750         this.basic.init(s);
37751     },
37752     
37753     getElementSize : function(s){
37754         return this.basic.getElementSize(s);
37755     },
37756     
37757     setElementSize : function(s, newSize, onComplete){
37758         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37759     },
37760     
37761     moveSplitter : function(s){
37762         var yes = Roo.bootstrap.SplitBar;
37763         switch(s.placement){
37764             case yes.LEFT:
37765                 s.el.setX(s.resizingEl.getRight());
37766                 break;
37767             case yes.RIGHT:
37768                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37769                 break;
37770             case yes.TOP:
37771                 s.el.setY(s.resizingEl.getBottom());
37772                 break;
37773             case yes.BOTTOM:
37774                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37775                 break;
37776         }
37777     }
37778 };
37779
37780 /**
37781  * Orientation constant - Create a vertical SplitBar
37782  * @static
37783  * @type Number
37784  */
37785 Roo.bootstrap.SplitBar.VERTICAL = 1;
37786
37787 /**
37788  * Orientation constant - Create a horizontal SplitBar
37789  * @static
37790  * @type Number
37791  */
37792 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37793
37794 /**
37795  * Placement constant - The resizing element is to the left of the splitter element
37796  * @static
37797  * @type Number
37798  */
37799 Roo.bootstrap.SplitBar.LEFT = 1;
37800
37801 /**
37802  * Placement constant - The resizing element is to the right of the splitter element
37803  * @static
37804  * @type Number
37805  */
37806 Roo.bootstrap.SplitBar.RIGHT = 2;
37807
37808 /**
37809  * Placement constant - The resizing element is positioned above the splitter element
37810  * @static
37811  * @type Number
37812  */
37813 Roo.bootstrap.SplitBar.TOP = 3;
37814
37815 /**
37816  * Placement constant - The resizing element is positioned under splitter element
37817  * @static
37818  * @type Number
37819  */
37820 Roo.bootstrap.SplitBar.BOTTOM = 4;
37821 Roo.namespace("Roo.bootstrap.layout");/*
37822  * Based on:
37823  * Ext JS Library 1.1.1
37824  * Copyright(c) 2006-2007, Ext JS, LLC.
37825  *
37826  * Originally Released Under LGPL - original licence link has changed is not relivant.
37827  *
37828  * Fork - LGPL
37829  * <script type="text/javascript">
37830  */
37831
37832 /**
37833  * @class Roo.bootstrap.layout.Manager
37834  * @extends Roo.bootstrap.Component
37835  * Base class for layout managers.
37836  */
37837 Roo.bootstrap.layout.Manager = function(config)
37838 {
37839     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37840
37841
37842
37843
37844
37845     /** false to disable window resize monitoring @type Boolean */
37846     this.monitorWindowResize = true;
37847     this.regions = {};
37848     this.addEvents({
37849         /**
37850          * @event layout
37851          * Fires when a layout is performed.
37852          * @param {Roo.LayoutManager} this
37853          */
37854         "layout" : true,
37855         /**
37856          * @event regionresized
37857          * Fires when the user resizes a region.
37858          * @param {Roo.LayoutRegion} region The resized region
37859          * @param {Number} newSize The new size (width for east/west, height for north/south)
37860          */
37861         "regionresized" : true,
37862         /**
37863          * @event regioncollapsed
37864          * Fires when a region is collapsed.
37865          * @param {Roo.LayoutRegion} region The collapsed region
37866          */
37867         "regioncollapsed" : true,
37868         /**
37869          * @event regionexpanded
37870          * Fires when a region is expanded.
37871          * @param {Roo.LayoutRegion} region The expanded region
37872          */
37873         "regionexpanded" : true
37874     });
37875     this.updating = false;
37876
37877     if (config.el) {
37878         this.el = Roo.get(config.el);
37879         this.initEvents();
37880     }
37881
37882 };
37883
37884 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37885
37886
37887     regions : null,
37888
37889     monitorWindowResize : true,
37890
37891
37892     updating : false,
37893
37894
37895     onRender : function(ct, position)
37896     {
37897         if(!this.el){
37898             this.el = Roo.get(ct);
37899             this.initEvents();
37900         }
37901         //this.fireEvent('render',this);
37902     },
37903
37904
37905     initEvents: function()
37906     {
37907
37908
37909         // ie scrollbar fix
37910         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37911             document.body.scroll = "no";
37912         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37913             this.el.position('relative');
37914         }
37915         this.id = this.el.id;
37916         this.el.addClass("roo-layout-container");
37917         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37918         if(this.el.dom != document.body ) {
37919             this.el.on('resize', this.layout,this);
37920             this.el.on('show', this.layout,this);
37921         }
37922
37923     },
37924
37925     /**
37926      * Returns true if this layout is currently being updated
37927      * @return {Boolean}
37928      */
37929     isUpdating : function(){
37930         return this.updating;
37931     },
37932
37933     /**
37934      * Suspend the LayoutManager from doing auto-layouts while
37935      * making multiple add or remove calls
37936      */
37937     beginUpdate : function(){
37938         this.updating = true;
37939     },
37940
37941     /**
37942      * Restore auto-layouts and optionally disable the manager from performing a layout
37943      * @param {Boolean} noLayout true to disable a layout update
37944      */
37945     endUpdate : function(noLayout){
37946         this.updating = false;
37947         if(!noLayout){
37948             this.layout();
37949         }
37950     },
37951
37952     layout: function(){
37953         // abstract...
37954     },
37955
37956     onRegionResized : function(region, newSize){
37957         this.fireEvent("regionresized", region, newSize);
37958         this.layout();
37959     },
37960
37961     onRegionCollapsed : function(region){
37962         this.fireEvent("regioncollapsed", region);
37963     },
37964
37965     onRegionExpanded : function(region){
37966         this.fireEvent("regionexpanded", region);
37967     },
37968
37969     /**
37970      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37971      * performs box-model adjustments.
37972      * @return {Object} The size as an object {width: (the width), height: (the height)}
37973      */
37974     getViewSize : function()
37975     {
37976         var size;
37977         if(this.el.dom != document.body){
37978             size = this.el.getSize();
37979         }else{
37980             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37981         }
37982         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37983         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37984         return size;
37985     },
37986
37987     /**
37988      * Returns the Element this layout is bound to.
37989      * @return {Roo.Element}
37990      */
37991     getEl : function(){
37992         return this.el;
37993     },
37994
37995     /**
37996      * Returns the specified region.
37997      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37998      * @return {Roo.LayoutRegion}
37999      */
38000     getRegion : function(target){
38001         return this.regions[target.toLowerCase()];
38002     },
38003
38004     onWindowResize : function(){
38005         if(this.monitorWindowResize){
38006             this.layout();
38007         }
38008     }
38009 });
38010 /*
38011  * Based on:
38012  * Ext JS Library 1.1.1
38013  * Copyright(c) 2006-2007, Ext JS, LLC.
38014  *
38015  * Originally Released Under LGPL - original licence link has changed is not relivant.
38016  *
38017  * Fork - LGPL
38018  * <script type="text/javascript">
38019  */
38020 /**
38021  * @class Roo.bootstrap.layout.Border
38022  * @extends Roo.bootstrap.layout.Manager
38023  * @builder-top
38024  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38025  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38026  * please see: examples/bootstrap/nested.html<br><br>
38027  
38028 <b>The container the layout is rendered into can be either the body element or any other element.
38029 If it is not the body element, the container needs to either be an absolute positioned element,
38030 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38031 the container size if it is not the body element.</b>
38032
38033 * @constructor
38034 * Create a new Border
38035 * @param {Object} config Configuration options
38036  */
38037 Roo.bootstrap.layout.Border = function(config){
38038     config = config || {};
38039     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38040     
38041     
38042     
38043     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38044         if(config[region]){
38045             config[region].region = region;
38046             this.addRegion(config[region]);
38047         }
38048     },this);
38049     
38050 };
38051
38052 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38053
38054 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38055     
38056         /**
38057          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38058          */
38059         /**
38060          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38061          */
38062         /**
38063          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38064          */
38065         /**
38066          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38067          */
38068         /**
38069          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38070          */
38071         
38072         
38073         
38074         
38075     parent : false, // this might point to a 'nest' or a ???
38076     
38077     /**
38078      * Creates and adds a new region if it doesn't already exist.
38079      * @param {String} target The target region key (north, south, east, west or center).
38080      * @param {Object} config The regions config object
38081      * @return {BorderLayoutRegion} The new region
38082      */
38083     addRegion : function(config)
38084     {
38085         if(!this.regions[config.region]){
38086             var r = this.factory(config);
38087             this.bindRegion(r);
38088         }
38089         return this.regions[config.region];
38090     },
38091
38092     // private (kinda)
38093     bindRegion : function(r){
38094         this.regions[r.config.region] = r;
38095         
38096         r.on("visibilitychange",    this.layout, this);
38097         r.on("paneladded",          this.layout, this);
38098         r.on("panelremoved",        this.layout, this);
38099         r.on("invalidated",         this.layout, this);
38100         r.on("resized",             this.onRegionResized, this);
38101         r.on("collapsed",           this.onRegionCollapsed, this);
38102         r.on("expanded",            this.onRegionExpanded, this);
38103     },
38104
38105     /**
38106      * Performs a layout update.
38107      */
38108     layout : function()
38109     {
38110         if(this.updating) {
38111             return;
38112         }
38113         
38114         // render all the rebions if they have not been done alreayd?
38115         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38116             if(this.regions[region] && !this.regions[region].bodyEl){
38117                 this.regions[region].onRender(this.el)
38118             }
38119         },this);
38120         
38121         var size = this.getViewSize();
38122         var w = size.width;
38123         var h = size.height;
38124         var centerW = w;
38125         var centerH = h;
38126         var centerY = 0;
38127         var centerX = 0;
38128         //var x = 0, y = 0;
38129
38130         var rs = this.regions;
38131         var north = rs["north"];
38132         var south = rs["south"]; 
38133         var west = rs["west"];
38134         var east = rs["east"];
38135         var center = rs["center"];
38136         //if(this.hideOnLayout){ // not supported anymore
38137             //c.el.setStyle("display", "none");
38138         //}
38139         if(north && north.isVisible()){
38140             var b = north.getBox();
38141             var m = north.getMargins();
38142             b.width = w - (m.left+m.right);
38143             b.x = m.left;
38144             b.y = m.top;
38145             centerY = b.height + b.y + m.bottom;
38146             centerH -= centerY;
38147             north.updateBox(this.safeBox(b));
38148         }
38149         if(south && south.isVisible()){
38150             var b = south.getBox();
38151             var m = south.getMargins();
38152             b.width = w - (m.left+m.right);
38153             b.x = m.left;
38154             var totalHeight = (b.height + m.top + m.bottom);
38155             b.y = h - totalHeight + m.top;
38156             centerH -= totalHeight;
38157             south.updateBox(this.safeBox(b));
38158         }
38159         if(west && west.isVisible()){
38160             var b = west.getBox();
38161             var m = west.getMargins();
38162             b.height = centerH - (m.top+m.bottom);
38163             b.x = m.left;
38164             b.y = centerY + m.top;
38165             var totalWidth = (b.width + m.left + m.right);
38166             centerX += totalWidth;
38167             centerW -= totalWidth;
38168             west.updateBox(this.safeBox(b));
38169         }
38170         if(east && east.isVisible()){
38171             var b = east.getBox();
38172             var m = east.getMargins();
38173             b.height = centerH - (m.top+m.bottom);
38174             var totalWidth = (b.width + m.left + m.right);
38175             b.x = w - totalWidth + m.left;
38176             b.y = centerY + m.top;
38177             centerW -= totalWidth;
38178             east.updateBox(this.safeBox(b));
38179         }
38180         if(center){
38181             var m = center.getMargins();
38182             var centerBox = {
38183                 x: centerX + m.left,
38184                 y: centerY + m.top,
38185                 width: centerW - (m.left+m.right),
38186                 height: centerH - (m.top+m.bottom)
38187             };
38188             //if(this.hideOnLayout){
38189                 //center.el.setStyle("display", "block");
38190             //}
38191             center.updateBox(this.safeBox(centerBox));
38192         }
38193         this.el.repaint();
38194         this.fireEvent("layout", this);
38195     },
38196
38197     // private
38198     safeBox : function(box){
38199         box.width = Math.max(0, box.width);
38200         box.height = Math.max(0, box.height);
38201         return box;
38202     },
38203
38204     /**
38205      * Adds a ContentPanel (or subclass) to this layout.
38206      * @param {String} target The target region key (north, south, east, west or center).
38207      * @param {Roo.ContentPanel} panel The panel to add
38208      * @return {Roo.ContentPanel} The added panel
38209      */
38210     add : function(target, panel){
38211          
38212         target = target.toLowerCase();
38213         return this.regions[target].add(panel);
38214     },
38215
38216     /**
38217      * Remove a ContentPanel (or subclass) to this layout.
38218      * @param {String} target The target region key (north, south, east, west or center).
38219      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38220      * @return {Roo.ContentPanel} The removed panel
38221      */
38222     remove : function(target, panel){
38223         target = target.toLowerCase();
38224         return this.regions[target].remove(panel);
38225     },
38226
38227     /**
38228      * Searches all regions for a panel with the specified id
38229      * @param {String} panelId
38230      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38231      */
38232     findPanel : function(panelId){
38233         var rs = this.regions;
38234         for(var target in rs){
38235             if(typeof rs[target] != "function"){
38236                 var p = rs[target].getPanel(panelId);
38237                 if(p){
38238                     return p;
38239                 }
38240             }
38241         }
38242         return null;
38243     },
38244
38245     /**
38246      * Searches all regions for a panel with the specified id and activates (shows) it.
38247      * @param {String/ContentPanel} panelId The panels id or the panel itself
38248      * @return {Roo.ContentPanel} The shown panel or null
38249      */
38250     showPanel : function(panelId) {
38251       var rs = this.regions;
38252       for(var target in rs){
38253          var r = rs[target];
38254          if(typeof r != "function"){
38255             if(r.hasPanel(panelId)){
38256                return r.showPanel(panelId);
38257             }
38258          }
38259       }
38260       return null;
38261    },
38262
38263    /**
38264      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38265      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38266      */
38267    /*
38268     restoreState : function(provider){
38269         if(!provider){
38270             provider = Roo.state.Manager;
38271         }
38272         var sm = new Roo.LayoutStateManager();
38273         sm.init(this, provider);
38274     },
38275 */
38276  
38277  
38278     /**
38279      * Adds a xtype elements to the layout.
38280      * <pre><code>
38281
38282 layout.addxtype({
38283        xtype : 'ContentPanel',
38284        region: 'west',
38285        items: [ .... ]
38286    }
38287 );
38288
38289 layout.addxtype({
38290         xtype : 'NestedLayoutPanel',
38291         region: 'west',
38292         layout: {
38293            center: { },
38294            west: { }   
38295         },
38296         items : [ ... list of content panels or nested layout panels.. ]
38297    }
38298 );
38299 </code></pre>
38300      * @param {Object} cfg Xtype definition of item to add.
38301      */
38302     addxtype : function(cfg)
38303     {
38304         // basically accepts a pannel...
38305         // can accept a layout region..!?!?
38306         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38307         
38308         
38309         // theory?  children can only be panels??
38310         
38311         //if (!cfg.xtype.match(/Panel$/)) {
38312         //    return false;
38313         //}
38314         var ret = false;
38315         
38316         if (typeof(cfg.region) == 'undefined') {
38317             Roo.log("Failed to add Panel, region was not set");
38318             Roo.log(cfg);
38319             return false;
38320         }
38321         var region = cfg.region;
38322         delete cfg.region;
38323         
38324           
38325         var xitems = [];
38326         if (cfg.items) {
38327             xitems = cfg.items;
38328             delete cfg.items;
38329         }
38330         var nb = false;
38331         
38332         if ( region == 'center') {
38333             Roo.log("Center: " + cfg.title);
38334         }
38335         
38336         
38337         switch(cfg.xtype) 
38338         {
38339             case 'Content':  // ContentPanel (el, cfg)
38340             case 'Scroll':  // ContentPanel (el, cfg)
38341             case 'View': 
38342                 cfg.autoCreate = cfg.autoCreate || true;
38343                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38344                 //} else {
38345                 //    var el = this.el.createChild();
38346                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38347                 //}
38348                 
38349                 this.add(region, ret);
38350                 break;
38351             
38352             /*
38353             case 'TreePanel': // our new panel!
38354                 cfg.el = this.el.createChild();
38355                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38356                 this.add(region, ret);
38357                 break;
38358             */
38359             
38360             case 'Nest': 
38361                 // create a new Layout (which is  a Border Layout...
38362                 
38363                 var clayout = cfg.layout;
38364                 clayout.el  = this.el.createChild();
38365                 clayout.items   = clayout.items  || [];
38366                 
38367                 delete cfg.layout;
38368                 
38369                 // replace this exitems with the clayout ones..
38370                 xitems = clayout.items;
38371                  
38372                 // force background off if it's in center...
38373                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38374                     cfg.background = false;
38375                 }
38376                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38377                 
38378                 
38379                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38380                 //console.log('adding nested layout panel '  + cfg.toSource());
38381                 this.add(region, ret);
38382                 nb = {}; /// find first...
38383                 break;
38384             
38385             case 'Grid':
38386                 
38387                 // needs grid and region
38388                 
38389                 //var el = this.getRegion(region).el.createChild();
38390                 /*
38391                  *var el = this.el.createChild();
38392                 // create the grid first...
38393                 cfg.grid.container = el;
38394                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38395                 */
38396                 
38397                 if (region == 'center' && this.active ) {
38398                     cfg.background = false;
38399                 }
38400                 
38401                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38402                 
38403                 this.add(region, ret);
38404                 /*
38405                 if (cfg.background) {
38406                     // render grid on panel activation (if panel background)
38407                     ret.on('activate', function(gp) {
38408                         if (!gp.grid.rendered) {
38409                     //        gp.grid.render(el);
38410                         }
38411                     });
38412                 } else {
38413                   //  cfg.grid.render(el);
38414                 }
38415                 */
38416                 break;
38417            
38418            
38419             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38420                 // it was the old xcomponent building that caused this before.
38421                 // espeically if border is the top element in the tree.
38422                 ret = this;
38423                 break; 
38424                 
38425                     
38426                 
38427                 
38428                 
38429             default:
38430                 /*
38431                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38432                     
38433                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38434                     this.add(region, ret);
38435                 } else {
38436                 */
38437                     Roo.log(cfg);
38438                     throw "Can not add '" + cfg.xtype + "' to Border";
38439                     return null;
38440              
38441                                 
38442              
38443         }
38444         this.beginUpdate();
38445         // add children..
38446         var region = '';
38447         var abn = {};
38448         Roo.each(xitems, function(i)  {
38449             region = nb && i.region ? i.region : false;
38450             
38451             var add = ret.addxtype(i);
38452            
38453             if (region) {
38454                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38455                 if (!i.background) {
38456                     abn[region] = nb[region] ;
38457                 }
38458             }
38459             
38460         });
38461         this.endUpdate();
38462
38463         // make the last non-background panel active..
38464         //if (nb) { Roo.log(abn); }
38465         if (nb) {
38466             
38467             for(var r in abn) {
38468                 region = this.getRegion(r);
38469                 if (region) {
38470                     // tried using nb[r], but it does not work..
38471                      
38472                     region.showPanel(abn[r]);
38473                    
38474                 }
38475             }
38476         }
38477         return ret;
38478         
38479     },
38480     
38481     
38482 // private
38483     factory : function(cfg)
38484     {
38485         
38486         var validRegions = Roo.bootstrap.layout.Border.regions;
38487
38488         var target = cfg.region;
38489         cfg.mgr = this;
38490         
38491         var r = Roo.bootstrap.layout;
38492         Roo.log(target);
38493         switch(target){
38494             case "north":
38495                 return new r.North(cfg);
38496             case "south":
38497                 return new r.South(cfg);
38498             case "east":
38499                 return new r.East(cfg);
38500             case "west":
38501                 return new r.West(cfg);
38502             case "center":
38503                 return new r.Center(cfg);
38504         }
38505         throw 'Layout region "'+target+'" not supported.';
38506     }
38507     
38508     
38509 });
38510  /*
38511  * Based on:
38512  * Ext JS Library 1.1.1
38513  * Copyright(c) 2006-2007, Ext JS, LLC.
38514  *
38515  * Originally Released Under LGPL - original licence link has changed is not relivant.
38516  *
38517  * Fork - LGPL
38518  * <script type="text/javascript">
38519  */
38520  
38521 /**
38522  * @class Roo.bootstrap.layout.Basic
38523  * @extends Roo.util.Observable
38524  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38525  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38526  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38527  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38528  * @cfg {string}   region  the region that it inhabits..
38529  * @cfg {bool}   skipConfig skip config?
38530  * 
38531
38532  */
38533 Roo.bootstrap.layout.Basic = function(config){
38534     
38535     this.mgr = config.mgr;
38536     
38537     this.position = config.region;
38538     
38539     var skipConfig = config.skipConfig;
38540     
38541     this.events = {
38542         /**
38543          * @scope Roo.BasicLayoutRegion
38544          */
38545         
38546         /**
38547          * @event beforeremove
38548          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38549          * @param {Roo.LayoutRegion} this
38550          * @param {Roo.ContentPanel} panel The panel
38551          * @param {Object} e The cancel event object
38552          */
38553         "beforeremove" : true,
38554         /**
38555          * @event invalidated
38556          * Fires when the layout for this region is changed.
38557          * @param {Roo.LayoutRegion} this
38558          */
38559         "invalidated" : true,
38560         /**
38561          * @event visibilitychange
38562          * Fires when this region is shown or hidden 
38563          * @param {Roo.LayoutRegion} this
38564          * @param {Boolean} visibility true or false
38565          */
38566         "visibilitychange" : true,
38567         /**
38568          * @event paneladded
38569          * Fires when a panel is added. 
38570          * @param {Roo.LayoutRegion} this
38571          * @param {Roo.ContentPanel} panel The panel
38572          */
38573         "paneladded" : true,
38574         /**
38575          * @event panelremoved
38576          * Fires when a panel is removed. 
38577          * @param {Roo.LayoutRegion} this
38578          * @param {Roo.ContentPanel} panel The panel
38579          */
38580         "panelremoved" : true,
38581         /**
38582          * @event beforecollapse
38583          * Fires when this region before collapse.
38584          * @param {Roo.LayoutRegion} this
38585          */
38586         "beforecollapse" : true,
38587         /**
38588          * @event collapsed
38589          * Fires when this region is collapsed.
38590          * @param {Roo.LayoutRegion} this
38591          */
38592         "collapsed" : true,
38593         /**
38594          * @event expanded
38595          * Fires when this region is expanded.
38596          * @param {Roo.LayoutRegion} this
38597          */
38598         "expanded" : true,
38599         /**
38600          * @event slideshow
38601          * Fires when this region is slid into view.
38602          * @param {Roo.LayoutRegion} this
38603          */
38604         "slideshow" : true,
38605         /**
38606          * @event slidehide
38607          * Fires when this region slides out of view. 
38608          * @param {Roo.LayoutRegion} this
38609          */
38610         "slidehide" : true,
38611         /**
38612          * @event panelactivated
38613          * Fires when a panel is activated. 
38614          * @param {Roo.LayoutRegion} this
38615          * @param {Roo.ContentPanel} panel The activated panel
38616          */
38617         "panelactivated" : true,
38618         /**
38619          * @event resized
38620          * Fires when the user resizes this region. 
38621          * @param {Roo.LayoutRegion} this
38622          * @param {Number} newSize The new size (width for east/west, height for north/south)
38623          */
38624         "resized" : true
38625     };
38626     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38627     this.panels = new Roo.util.MixedCollection();
38628     this.panels.getKey = this.getPanelId.createDelegate(this);
38629     this.box = null;
38630     this.activePanel = null;
38631     // ensure listeners are added...
38632     
38633     if (config.listeners || config.events) {
38634         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38635             listeners : config.listeners || {},
38636             events : config.events || {}
38637         });
38638     }
38639     
38640     if(skipConfig !== true){
38641         this.applyConfig(config);
38642     }
38643 };
38644
38645 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38646 {
38647     getPanelId : function(p){
38648         return p.getId();
38649     },
38650     
38651     applyConfig : function(config){
38652         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38653         this.config = config;
38654         
38655     },
38656     
38657     /**
38658      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38659      * the width, for horizontal (north, south) the height.
38660      * @param {Number} newSize The new width or height
38661      */
38662     resizeTo : function(newSize){
38663         var el = this.el ? this.el :
38664                  (this.activePanel ? this.activePanel.getEl() : null);
38665         if(el){
38666             switch(this.position){
38667                 case "east":
38668                 case "west":
38669                     el.setWidth(newSize);
38670                     this.fireEvent("resized", this, newSize);
38671                 break;
38672                 case "north":
38673                 case "south":
38674                     el.setHeight(newSize);
38675                     this.fireEvent("resized", this, newSize);
38676                 break;                
38677             }
38678         }
38679     },
38680     
38681     getBox : function(){
38682         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38683     },
38684     
38685     getMargins : function(){
38686         return this.margins;
38687     },
38688     
38689     updateBox : function(box){
38690         this.box = box;
38691         var el = this.activePanel.getEl();
38692         el.dom.style.left = box.x + "px";
38693         el.dom.style.top = box.y + "px";
38694         this.activePanel.setSize(box.width, box.height);
38695     },
38696     
38697     /**
38698      * Returns the container element for this region.
38699      * @return {Roo.Element}
38700      */
38701     getEl : function(){
38702         return this.activePanel;
38703     },
38704     
38705     /**
38706      * Returns true if this region is currently visible.
38707      * @return {Boolean}
38708      */
38709     isVisible : function(){
38710         return this.activePanel ? true : false;
38711     },
38712     
38713     setActivePanel : function(panel){
38714         panel = this.getPanel(panel);
38715         if(this.activePanel && this.activePanel != panel){
38716             this.activePanel.setActiveState(false);
38717             this.activePanel.getEl().setLeftTop(-10000,-10000);
38718         }
38719         this.activePanel = panel;
38720         panel.setActiveState(true);
38721         if(this.box){
38722             panel.setSize(this.box.width, this.box.height);
38723         }
38724         this.fireEvent("panelactivated", this, panel);
38725         this.fireEvent("invalidated");
38726     },
38727     
38728     /**
38729      * Show the specified panel.
38730      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38731      * @return {Roo.ContentPanel} The shown panel or null
38732      */
38733     showPanel : function(panel){
38734         panel = this.getPanel(panel);
38735         if(panel){
38736             this.setActivePanel(panel);
38737         }
38738         return panel;
38739     },
38740     
38741     /**
38742      * Get the active panel for this region.
38743      * @return {Roo.ContentPanel} The active panel or null
38744      */
38745     getActivePanel : function(){
38746         return this.activePanel;
38747     },
38748     
38749     /**
38750      * Add the passed ContentPanel(s)
38751      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38752      * @return {Roo.ContentPanel} The panel added (if only one was added)
38753      */
38754     add : function(panel){
38755         if(arguments.length > 1){
38756             for(var i = 0, len = arguments.length; i < len; i++) {
38757                 this.add(arguments[i]);
38758             }
38759             return null;
38760         }
38761         if(this.hasPanel(panel)){
38762             this.showPanel(panel);
38763             return panel;
38764         }
38765         var el = panel.getEl();
38766         if(el.dom.parentNode != this.mgr.el.dom){
38767             this.mgr.el.dom.appendChild(el.dom);
38768         }
38769         if(panel.setRegion){
38770             panel.setRegion(this);
38771         }
38772         this.panels.add(panel);
38773         el.setStyle("position", "absolute");
38774         if(!panel.background){
38775             this.setActivePanel(panel);
38776             if(this.config.initialSize && this.panels.getCount()==1){
38777                 this.resizeTo(this.config.initialSize);
38778             }
38779         }
38780         this.fireEvent("paneladded", this, panel);
38781         return panel;
38782     },
38783     
38784     /**
38785      * Returns true if the panel is in this region.
38786      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38787      * @return {Boolean}
38788      */
38789     hasPanel : function(panel){
38790         if(typeof panel == "object"){ // must be panel obj
38791             panel = panel.getId();
38792         }
38793         return this.getPanel(panel) ? true : false;
38794     },
38795     
38796     /**
38797      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38798      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38799      * @param {Boolean} preservePanel Overrides the config preservePanel option
38800      * @return {Roo.ContentPanel} The panel that was removed
38801      */
38802     remove : function(panel, preservePanel){
38803         panel = this.getPanel(panel);
38804         if(!panel){
38805             return null;
38806         }
38807         var e = {};
38808         this.fireEvent("beforeremove", this, panel, e);
38809         if(e.cancel === true){
38810             return null;
38811         }
38812         var panelId = panel.getId();
38813         this.panels.removeKey(panelId);
38814         return panel;
38815     },
38816     
38817     /**
38818      * Returns the panel specified or null if it's not in this region.
38819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38820      * @return {Roo.ContentPanel}
38821      */
38822     getPanel : function(id){
38823         if(typeof id == "object"){ // must be panel obj
38824             return id;
38825         }
38826         return this.panels.get(id);
38827     },
38828     
38829     /**
38830      * Returns this regions position (north/south/east/west/center).
38831      * @return {String} 
38832      */
38833     getPosition: function(){
38834         return this.position;    
38835     }
38836 });/*
38837  * Based on:
38838  * Ext JS Library 1.1.1
38839  * Copyright(c) 2006-2007, Ext JS, LLC.
38840  *
38841  * Originally Released Under LGPL - original licence link has changed is not relivant.
38842  *
38843  * Fork - LGPL
38844  * <script type="text/javascript">
38845  */
38846  
38847 /**
38848  * @class Roo.bootstrap.layout.Region
38849  * @extends Roo.bootstrap.layout.Basic
38850  * This class represents a region in a layout manager.
38851  
38852  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38853  * @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})
38854  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38855  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38856  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38857  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38858  * @cfg {String}    title           The title for the region (overrides panel titles)
38859  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38860  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38861  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38862  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38863  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38864  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38865  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38866  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38867  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38868  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38869
38870  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38871  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38872  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38873  * @cfg {Number}    width           For East/West panels
38874  * @cfg {Number}    height          For North/South panels
38875  * @cfg {Boolean}   split           To show the splitter
38876  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38877  * 
38878  * @cfg {string}   cls             Extra CSS classes to add to region
38879  * 
38880  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38881  * @cfg {string}   region  the region that it inhabits..
38882  *
38883
38884  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38885  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38886
38887  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38888  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38889  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38890  */
38891 Roo.bootstrap.layout.Region = function(config)
38892 {
38893     this.applyConfig(config);
38894
38895     var mgr = config.mgr;
38896     var pos = config.region;
38897     config.skipConfig = true;
38898     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38899     
38900     if (mgr.el) {
38901         this.onRender(mgr.el);   
38902     }
38903      
38904     this.visible = true;
38905     this.collapsed = false;
38906     this.unrendered_panels = [];
38907 };
38908
38909 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38910
38911     position: '', // set by wrapper (eg. north/south etc..)
38912     unrendered_panels : null,  // unrendered panels.
38913     
38914     tabPosition : false,
38915     
38916     mgr: false, // points to 'Border'
38917     
38918     
38919     createBody : function(){
38920         /** This region's body element 
38921         * @type Roo.Element */
38922         this.bodyEl = this.el.createChild({
38923                 tag: "div",
38924                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38925         });
38926     },
38927
38928     onRender: function(ctr, pos)
38929     {
38930         var dh = Roo.DomHelper;
38931         /** This region's container element 
38932         * @type Roo.Element */
38933         this.el = dh.append(ctr.dom, {
38934                 tag: "div",
38935                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38936             }, true);
38937         /** This region's title element 
38938         * @type Roo.Element */
38939     
38940         this.titleEl = dh.append(this.el.dom,  {
38941                 tag: "div",
38942                 unselectable: "on",
38943                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38944                 children:[
38945                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38946                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38947                 ]
38948             }, true);
38949         
38950         this.titleEl.enableDisplayMode();
38951         /** This region's title text element 
38952         * @type HTMLElement */
38953         this.titleTextEl = this.titleEl.dom.firstChild;
38954         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38955         /*
38956         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38957         this.closeBtn.enableDisplayMode();
38958         this.closeBtn.on("click", this.closeClicked, this);
38959         this.closeBtn.hide();
38960     */
38961         this.createBody(this.config);
38962         if(this.config.hideWhenEmpty){
38963             this.hide();
38964             this.on("paneladded", this.validateVisibility, this);
38965             this.on("panelremoved", this.validateVisibility, this);
38966         }
38967         if(this.autoScroll){
38968             this.bodyEl.setStyle("overflow", "auto");
38969         }else{
38970             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38971         }
38972         //if(c.titlebar !== false){
38973             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38974                 this.titleEl.hide();
38975             }else{
38976                 this.titleEl.show();
38977                 if(this.config.title){
38978                     this.titleTextEl.innerHTML = this.config.title;
38979                 }
38980             }
38981         //}
38982         if(this.config.collapsed){
38983             this.collapse(true);
38984         }
38985         if(this.config.hidden){
38986             this.hide();
38987         }
38988         
38989         if (this.unrendered_panels && this.unrendered_panels.length) {
38990             for (var i =0;i< this.unrendered_panels.length; i++) {
38991                 this.add(this.unrendered_panels[i]);
38992             }
38993             this.unrendered_panels = null;
38994             
38995         }
38996         
38997     },
38998     
38999     applyConfig : function(c)
39000     {
39001         /*
39002          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39003             var dh = Roo.DomHelper;
39004             if(c.titlebar !== false){
39005                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39006                 this.collapseBtn.on("click", this.collapse, this);
39007                 this.collapseBtn.enableDisplayMode();
39008                 /*
39009                 if(c.showPin === true || this.showPin){
39010                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39011                     this.stickBtn.enableDisplayMode();
39012                     this.stickBtn.on("click", this.expand, this);
39013                     this.stickBtn.hide();
39014                 }
39015                 
39016             }
39017             */
39018             /** This region's collapsed element
39019             * @type Roo.Element */
39020             /*
39021              *
39022             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39023                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39024             ]}, true);
39025             
39026             if(c.floatable !== false){
39027                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39028                this.collapsedEl.on("click", this.collapseClick, this);
39029             }
39030
39031             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39032                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39033                    id: "message", unselectable: "on", style:{"float":"left"}});
39034                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39035              }
39036             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39037             this.expandBtn.on("click", this.expand, this);
39038             
39039         }
39040         
39041         if(this.collapseBtn){
39042             this.collapseBtn.setVisible(c.collapsible == true);
39043         }
39044         
39045         this.cmargins = c.cmargins || this.cmargins ||
39046                          (this.position == "west" || this.position == "east" ?
39047                              {top: 0, left: 2, right:2, bottom: 0} :
39048                              {top: 2, left: 0, right:0, bottom: 2});
39049         */
39050         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39051         
39052         
39053         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39054         
39055         this.autoScroll = c.autoScroll || false;
39056         
39057         
39058        
39059         
39060         this.duration = c.duration || .30;
39061         this.slideDuration = c.slideDuration || .45;
39062         this.config = c;
39063        
39064     },
39065     /**
39066      * Returns true if this region is currently visible.
39067      * @return {Boolean}
39068      */
39069     isVisible : function(){
39070         return this.visible;
39071     },
39072
39073     /**
39074      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39075      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39076      */
39077     //setCollapsedTitle : function(title){
39078     //    title = title || "&#160;";
39079      //   if(this.collapsedTitleTextEl){
39080       //      this.collapsedTitleTextEl.innerHTML = title;
39081        // }
39082     //},
39083
39084     getBox : function(){
39085         var b;
39086       //  if(!this.collapsed){
39087             b = this.el.getBox(false, true);
39088        // }else{
39089           //  b = this.collapsedEl.getBox(false, true);
39090         //}
39091         return b;
39092     },
39093
39094     getMargins : function(){
39095         return this.margins;
39096         //return this.collapsed ? this.cmargins : this.margins;
39097     },
39098 /*
39099     highlight : function(){
39100         this.el.addClass("x-layout-panel-dragover");
39101     },
39102
39103     unhighlight : function(){
39104         this.el.removeClass("x-layout-panel-dragover");
39105     },
39106 */
39107     updateBox : function(box)
39108     {
39109         if (!this.bodyEl) {
39110             return; // not rendered yet..
39111         }
39112         
39113         this.box = box;
39114         if(!this.collapsed){
39115             this.el.dom.style.left = box.x + "px";
39116             this.el.dom.style.top = box.y + "px";
39117             this.updateBody(box.width, box.height);
39118         }else{
39119             this.collapsedEl.dom.style.left = box.x + "px";
39120             this.collapsedEl.dom.style.top = box.y + "px";
39121             this.collapsedEl.setSize(box.width, box.height);
39122         }
39123         if(this.tabs){
39124             this.tabs.autoSizeTabs();
39125         }
39126     },
39127
39128     updateBody : function(w, h)
39129     {
39130         if(w !== null){
39131             this.el.setWidth(w);
39132             w -= this.el.getBorderWidth("rl");
39133             if(this.config.adjustments){
39134                 w += this.config.adjustments[0];
39135             }
39136         }
39137         if(h !== null && h > 0){
39138             this.el.setHeight(h);
39139             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39140             h -= this.el.getBorderWidth("tb");
39141             if(this.config.adjustments){
39142                 h += this.config.adjustments[1];
39143             }
39144             this.bodyEl.setHeight(h);
39145             if(this.tabs){
39146                 h = this.tabs.syncHeight(h);
39147             }
39148         }
39149         if(this.panelSize){
39150             w = w !== null ? w : this.panelSize.width;
39151             h = h !== null ? h : this.panelSize.height;
39152         }
39153         if(this.activePanel){
39154             var el = this.activePanel.getEl();
39155             w = w !== null ? w : el.getWidth();
39156             h = h !== null ? h : el.getHeight();
39157             this.panelSize = {width: w, height: h};
39158             this.activePanel.setSize(w, h);
39159         }
39160         if(Roo.isIE && this.tabs){
39161             this.tabs.el.repaint();
39162         }
39163     },
39164
39165     /**
39166      * Returns the container element for this region.
39167      * @return {Roo.Element}
39168      */
39169     getEl : function(){
39170         return this.el;
39171     },
39172
39173     /**
39174      * Hides this region.
39175      */
39176     hide : function(){
39177         //if(!this.collapsed){
39178             this.el.dom.style.left = "-2000px";
39179             this.el.hide();
39180         //}else{
39181          //   this.collapsedEl.dom.style.left = "-2000px";
39182          //   this.collapsedEl.hide();
39183        // }
39184         this.visible = false;
39185         this.fireEvent("visibilitychange", this, false);
39186     },
39187
39188     /**
39189      * Shows this region if it was previously hidden.
39190      */
39191     show : function(){
39192         //if(!this.collapsed){
39193             this.el.show();
39194         //}else{
39195         //    this.collapsedEl.show();
39196        // }
39197         this.visible = true;
39198         this.fireEvent("visibilitychange", this, true);
39199     },
39200 /*
39201     closeClicked : function(){
39202         if(this.activePanel){
39203             this.remove(this.activePanel);
39204         }
39205     },
39206
39207     collapseClick : function(e){
39208         if(this.isSlid){
39209            e.stopPropagation();
39210            this.slideIn();
39211         }else{
39212            e.stopPropagation();
39213            this.slideOut();
39214         }
39215     },
39216 */
39217     /**
39218      * Collapses this region.
39219      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39220      */
39221     /*
39222     collapse : function(skipAnim, skipCheck = false){
39223         if(this.collapsed) {
39224             return;
39225         }
39226         
39227         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39228             
39229             this.collapsed = true;
39230             if(this.split){
39231                 this.split.el.hide();
39232             }
39233             if(this.config.animate && skipAnim !== true){
39234                 this.fireEvent("invalidated", this);
39235                 this.animateCollapse();
39236             }else{
39237                 this.el.setLocation(-20000,-20000);
39238                 this.el.hide();
39239                 this.collapsedEl.show();
39240                 this.fireEvent("collapsed", this);
39241                 this.fireEvent("invalidated", this);
39242             }
39243         }
39244         
39245     },
39246 */
39247     animateCollapse : function(){
39248         // overridden
39249     },
39250
39251     /**
39252      * Expands this region if it was previously collapsed.
39253      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39254      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39255      */
39256     /*
39257     expand : function(e, skipAnim){
39258         if(e) {
39259             e.stopPropagation();
39260         }
39261         if(!this.collapsed || this.el.hasActiveFx()) {
39262             return;
39263         }
39264         if(this.isSlid){
39265             this.afterSlideIn();
39266             skipAnim = true;
39267         }
39268         this.collapsed = false;
39269         if(this.config.animate && skipAnim !== true){
39270             this.animateExpand();
39271         }else{
39272             this.el.show();
39273             if(this.split){
39274                 this.split.el.show();
39275             }
39276             this.collapsedEl.setLocation(-2000,-2000);
39277             this.collapsedEl.hide();
39278             this.fireEvent("invalidated", this);
39279             this.fireEvent("expanded", this);
39280         }
39281     },
39282 */
39283     animateExpand : function(){
39284         // overridden
39285     },
39286
39287     initTabs : function()
39288     {
39289         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39290         
39291         var ts = new Roo.bootstrap.panel.Tabs({
39292             el: this.bodyEl.dom,
39293             region : this,
39294             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39295             disableTooltips: this.config.disableTabTips,
39296             toolbar : this.config.toolbar
39297         });
39298         
39299         if(this.config.hideTabs){
39300             ts.stripWrap.setDisplayed(false);
39301         }
39302         this.tabs = ts;
39303         ts.resizeTabs = this.config.resizeTabs === true;
39304         ts.minTabWidth = this.config.minTabWidth || 40;
39305         ts.maxTabWidth = this.config.maxTabWidth || 250;
39306         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39307         ts.monitorResize = false;
39308         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39309         ts.bodyEl.addClass('roo-layout-tabs-body');
39310         this.panels.each(this.initPanelAsTab, this);
39311     },
39312
39313     initPanelAsTab : function(panel){
39314         var ti = this.tabs.addTab(
39315             panel.getEl().id,
39316             panel.getTitle(),
39317             null,
39318             this.config.closeOnTab && panel.isClosable(),
39319             panel.tpl
39320         );
39321         if(panel.tabTip !== undefined){
39322             ti.setTooltip(panel.tabTip);
39323         }
39324         ti.on("activate", function(){
39325               this.setActivePanel(panel);
39326         }, this);
39327         
39328         if(this.config.closeOnTab){
39329             ti.on("beforeclose", function(t, e){
39330                 e.cancel = true;
39331                 this.remove(panel);
39332             }, this);
39333         }
39334         
39335         panel.tabItem = ti;
39336         
39337         return ti;
39338     },
39339
39340     updatePanelTitle : function(panel, title)
39341     {
39342         if(this.activePanel == panel){
39343             this.updateTitle(title);
39344         }
39345         if(this.tabs){
39346             var ti = this.tabs.getTab(panel.getEl().id);
39347             ti.setText(title);
39348             if(panel.tabTip !== undefined){
39349                 ti.setTooltip(panel.tabTip);
39350             }
39351         }
39352     },
39353
39354     updateTitle : function(title){
39355         if(this.titleTextEl && !this.config.title){
39356             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39357         }
39358     },
39359
39360     setActivePanel : function(panel)
39361     {
39362         panel = this.getPanel(panel);
39363         if(this.activePanel && this.activePanel != panel){
39364             if(this.activePanel.setActiveState(false) === false){
39365                 return;
39366             }
39367         }
39368         this.activePanel = panel;
39369         panel.setActiveState(true);
39370         if(this.panelSize){
39371             panel.setSize(this.panelSize.width, this.panelSize.height);
39372         }
39373         if(this.closeBtn){
39374             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39375         }
39376         this.updateTitle(panel.getTitle());
39377         if(this.tabs){
39378             this.fireEvent("invalidated", this);
39379         }
39380         this.fireEvent("panelactivated", this, panel);
39381     },
39382
39383     /**
39384      * Shows the specified panel.
39385      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39386      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39387      */
39388     showPanel : function(panel)
39389     {
39390         panel = this.getPanel(panel);
39391         if(panel){
39392             if(this.tabs){
39393                 var tab = this.tabs.getTab(panel.getEl().id);
39394                 if(tab.isHidden()){
39395                     this.tabs.unhideTab(tab.id);
39396                 }
39397                 tab.activate();
39398             }else{
39399                 this.setActivePanel(panel);
39400             }
39401         }
39402         return panel;
39403     },
39404
39405     /**
39406      * Get the active panel for this region.
39407      * @return {Roo.ContentPanel} The active panel or null
39408      */
39409     getActivePanel : function(){
39410         return this.activePanel;
39411     },
39412
39413     validateVisibility : function(){
39414         if(this.panels.getCount() < 1){
39415             this.updateTitle("&#160;");
39416             this.closeBtn.hide();
39417             this.hide();
39418         }else{
39419             if(!this.isVisible()){
39420                 this.show();
39421             }
39422         }
39423     },
39424
39425     /**
39426      * Adds the passed ContentPanel(s) to this region.
39427      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39428      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39429      */
39430     add : function(panel)
39431     {
39432         if(arguments.length > 1){
39433             for(var i = 0, len = arguments.length; i < len; i++) {
39434                 this.add(arguments[i]);
39435             }
39436             return null;
39437         }
39438         
39439         // if we have not been rendered yet, then we can not really do much of this..
39440         if (!this.bodyEl) {
39441             this.unrendered_panels.push(panel);
39442             return panel;
39443         }
39444         
39445         
39446         
39447         
39448         if(this.hasPanel(panel)){
39449             this.showPanel(panel);
39450             return panel;
39451         }
39452         panel.setRegion(this);
39453         this.panels.add(panel);
39454        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39455             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39456             // and hide them... ???
39457             this.bodyEl.dom.appendChild(panel.getEl().dom);
39458             if(panel.background !== true){
39459                 this.setActivePanel(panel);
39460             }
39461             this.fireEvent("paneladded", this, panel);
39462             return panel;
39463         }
39464         */
39465         if(!this.tabs){
39466             this.initTabs();
39467         }else{
39468             this.initPanelAsTab(panel);
39469         }
39470         
39471         
39472         if(panel.background !== true){
39473             this.tabs.activate(panel.getEl().id);
39474         }
39475         this.fireEvent("paneladded", this, panel);
39476         return panel;
39477     },
39478
39479     /**
39480      * Hides the tab for the specified panel.
39481      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39482      */
39483     hidePanel : function(panel){
39484         if(this.tabs && (panel = this.getPanel(panel))){
39485             this.tabs.hideTab(panel.getEl().id);
39486         }
39487     },
39488
39489     /**
39490      * Unhides the tab for a previously hidden panel.
39491      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39492      */
39493     unhidePanel : function(panel){
39494         if(this.tabs && (panel = this.getPanel(panel))){
39495             this.tabs.unhideTab(panel.getEl().id);
39496         }
39497     },
39498
39499     clearPanels : function(){
39500         while(this.panels.getCount() > 0){
39501              this.remove(this.panels.first());
39502         }
39503     },
39504
39505     /**
39506      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39507      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39508      * @param {Boolean} preservePanel Overrides the config preservePanel option
39509      * @return {Roo.ContentPanel} The panel that was removed
39510      */
39511     remove : function(panel, preservePanel)
39512     {
39513         panel = this.getPanel(panel);
39514         if(!panel){
39515             return null;
39516         }
39517         var e = {};
39518         this.fireEvent("beforeremove", this, panel, e);
39519         if(e.cancel === true){
39520             return null;
39521         }
39522         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39523         var panelId = panel.getId();
39524         this.panels.removeKey(panelId);
39525         if(preservePanel){
39526             document.body.appendChild(panel.getEl().dom);
39527         }
39528         if(this.tabs){
39529             this.tabs.removeTab(panel.getEl().id);
39530         }else if (!preservePanel){
39531             this.bodyEl.dom.removeChild(panel.getEl().dom);
39532         }
39533         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39534             var p = this.panels.first();
39535             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39536             tempEl.appendChild(p.getEl().dom);
39537             this.bodyEl.update("");
39538             this.bodyEl.dom.appendChild(p.getEl().dom);
39539             tempEl = null;
39540             this.updateTitle(p.getTitle());
39541             this.tabs = null;
39542             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39543             this.setActivePanel(p);
39544         }
39545         panel.setRegion(null);
39546         if(this.activePanel == panel){
39547             this.activePanel = null;
39548         }
39549         if(this.config.autoDestroy !== false && preservePanel !== true){
39550             try{panel.destroy();}catch(e){}
39551         }
39552         this.fireEvent("panelremoved", this, panel);
39553         return panel;
39554     },
39555
39556     /**
39557      * Returns the TabPanel component used by this region
39558      * @return {Roo.TabPanel}
39559      */
39560     getTabs : function(){
39561         return this.tabs;
39562     },
39563
39564     createTool : function(parentEl, className){
39565         var btn = Roo.DomHelper.append(parentEl, {
39566             tag: "div",
39567             cls: "x-layout-tools-button",
39568             children: [ {
39569                 tag: "div",
39570                 cls: "roo-layout-tools-button-inner " + className,
39571                 html: "&#160;"
39572             }]
39573         }, true);
39574         btn.addClassOnOver("roo-layout-tools-button-over");
39575         return btn;
39576     }
39577 });/*
39578  * Based on:
39579  * Ext JS Library 1.1.1
39580  * Copyright(c) 2006-2007, Ext JS, LLC.
39581  *
39582  * Originally Released Under LGPL - original licence link has changed is not relivant.
39583  *
39584  * Fork - LGPL
39585  * <script type="text/javascript">
39586  */
39587  
39588
39589
39590 /**
39591  * @class Roo.SplitLayoutRegion
39592  * @extends Roo.LayoutRegion
39593  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39594  */
39595 Roo.bootstrap.layout.Split = function(config){
39596     this.cursor = config.cursor;
39597     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39598 };
39599
39600 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39601 {
39602     splitTip : "Drag to resize.",
39603     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39604     useSplitTips : false,
39605
39606     applyConfig : function(config){
39607         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39608     },
39609     
39610     onRender : function(ctr,pos) {
39611         
39612         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39613         if(!this.config.split){
39614             return;
39615         }
39616         if(!this.split){
39617             
39618             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39619                             tag: "div",
39620                             id: this.el.id + "-split",
39621                             cls: "roo-layout-split roo-layout-split-"+this.position,
39622                             html: "&#160;"
39623             });
39624             /** The SplitBar for this region 
39625             * @type Roo.SplitBar */
39626             // does not exist yet...
39627             Roo.log([this.position, this.orientation]);
39628             
39629             this.split = new Roo.bootstrap.SplitBar({
39630                 dragElement : splitEl,
39631                 resizingElement: this.el,
39632                 orientation : this.orientation
39633             });
39634             
39635             this.split.on("moved", this.onSplitMove, this);
39636             this.split.useShim = this.config.useShim === true;
39637             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39638             if(this.useSplitTips){
39639                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39640             }
39641             //if(config.collapsible){
39642             //    this.split.el.on("dblclick", this.collapse,  this);
39643             //}
39644         }
39645         if(typeof this.config.minSize != "undefined"){
39646             this.split.minSize = this.config.minSize;
39647         }
39648         if(typeof this.config.maxSize != "undefined"){
39649             this.split.maxSize = this.config.maxSize;
39650         }
39651         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39652             this.hideSplitter();
39653         }
39654         
39655     },
39656
39657     getHMaxSize : function(){
39658          var cmax = this.config.maxSize || 10000;
39659          var center = this.mgr.getRegion("center");
39660          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39661     },
39662
39663     getVMaxSize : function(){
39664          var cmax = this.config.maxSize || 10000;
39665          var center = this.mgr.getRegion("center");
39666          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39667     },
39668
39669     onSplitMove : function(split, newSize){
39670         this.fireEvent("resized", this, newSize);
39671     },
39672     
39673     /** 
39674      * Returns the {@link Roo.SplitBar} for this region.
39675      * @return {Roo.SplitBar}
39676      */
39677     getSplitBar : function(){
39678         return this.split;
39679     },
39680     
39681     hide : function(){
39682         this.hideSplitter();
39683         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39684     },
39685
39686     hideSplitter : function(){
39687         if(this.split){
39688             this.split.el.setLocation(-2000,-2000);
39689             this.split.el.hide();
39690         }
39691     },
39692
39693     show : function(){
39694         if(this.split){
39695             this.split.el.show();
39696         }
39697         Roo.bootstrap.layout.Split.superclass.show.call(this);
39698     },
39699     
39700     beforeSlide: function(){
39701         if(Roo.isGecko){// firefox overflow auto bug workaround
39702             this.bodyEl.clip();
39703             if(this.tabs) {
39704                 this.tabs.bodyEl.clip();
39705             }
39706             if(this.activePanel){
39707                 this.activePanel.getEl().clip();
39708                 
39709                 if(this.activePanel.beforeSlide){
39710                     this.activePanel.beforeSlide();
39711                 }
39712             }
39713         }
39714     },
39715     
39716     afterSlide : function(){
39717         if(Roo.isGecko){// firefox overflow auto bug workaround
39718             this.bodyEl.unclip();
39719             if(this.tabs) {
39720                 this.tabs.bodyEl.unclip();
39721             }
39722             if(this.activePanel){
39723                 this.activePanel.getEl().unclip();
39724                 if(this.activePanel.afterSlide){
39725                     this.activePanel.afterSlide();
39726                 }
39727             }
39728         }
39729     },
39730
39731     initAutoHide : function(){
39732         if(this.autoHide !== false){
39733             if(!this.autoHideHd){
39734                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39735                 this.autoHideHd = {
39736                     "mouseout": function(e){
39737                         if(!e.within(this.el, true)){
39738                             st.delay(500);
39739                         }
39740                     },
39741                     "mouseover" : function(e){
39742                         st.cancel();
39743                     },
39744                     scope : this
39745                 };
39746             }
39747             this.el.on(this.autoHideHd);
39748         }
39749     },
39750
39751     clearAutoHide : function(){
39752         if(this.autoHide !== false){
39753             this.el.un("mouseout", this.autoHideHd.mouseout);
39754             this.el.un("mouseover", this.autoHideHd.mouseover);
39755         }
39756     },
39757
39758     clearMonitor : function(){
39759         Roo.get(document).un("click", this.slideInIf, this);
39760     },
39761
39762     // these names are backwards but not changed for compat
39763     slideOut : function(){
39764         if(this.isSlid || this.el.hasActiveFx()){
39765             return;
39766         }
39767         this.isSlid = true;
39768         if(this.collapseBtn){
39769             this.collapseBtn.hide();
39770         }
39771         this.closeBtnState = this.closeBtn.getStyle('display');
39772         this.closeBtn.hide();
39773         if(this.stickBtn){
39774             this.stickBtn.show();
39775         }
39776         this.el.show();
39777         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39778         this.beforeSlide();
39779         this.el.setStyle("z-index", 10001);
39780         this.el.slideIn(this.getSlideAnchor(), {
39781             callback: function(){
39782                 this.afterSlide();
39783                 this.initAutoHide();
39784                 Roo.get(document).on("click", this.slideInIf, this);
39785                 this.fireEvent("slideshow", this);
39786             },
39787             scope: this,
39788             block: true
39789         });
39790     },
39791
39792     afterSlideIn : function(){
39793         this.clearAutoHide();
39794         this.isSlid = false;
39795         this.clearMonitor();
39796         this.el.setStyle("z-index", "");
39797         if(this.collapseBtn){
39798             this.collapseBtn.show();
39799         }
39800         this.closeBtn.setStyle('display', this.closeBtnState);
39801         if(this.stickBtn){
39802             this.stickBtn.hide();
39803         }
39804         this.fireEvent("slidehide", this);
39805     },
39806
39807     slideIn : function(cb){
39808         if(!this.isSlid || this.el.hasActiveFx()){
39809             Roo.callback(cb);
39810             return;
39811         }
39812         this.isSlid = false;
39813         this.beforeSlide();
39814         this.el.slideOut(this.getSlideAnchor(), {
39815             callback: function(){
39816                 this.el.setLeftTop(-10000, -10000);
39817                 this.afterSlide();
39818                 this.afterSlideIn();
39819                 Roo.callback(cb);
39820             },
39821             scope: this,
39822             block: true
39823         });
39824     },
39825     
39826     slideInIf : function(e){
39827         if(!e.within(this.el)){
39828             this.slideIn();
39829         }
39830     },
39831
39832     animateCollapse : function(){
39833         this.beforeSlide();
39834         this.el.setStyle("z-index", 20000);
39835         var anchor = this.getSlideAnchor();
39836         this.el.slideOut(anchor, {
39837             callback : function(){
39838                 this.el.setStyle("z-index", "");
39839                 this.collapsedEl.slideIn(anchor, {duration:.3});
39840                 this.afterSlide();
39841                 this.el.setLocation(-10000,-10000);
39842                 this.el.hide();
39843                 this.fireEvent("collapsed", this);
39844             },
39845             scope: this,
39846             block: true
39847         });
39848     },
39849
39850     animateExpand : function(){
39851         this.beforeSlide();
39852         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39853         this.el.setStyle("z-index", 20000);
39854         this.collapsedEl.hide({
39855             duration:.1
39856         });
39857         this.el.slideIn(this.getSlideAnchor(), {
39858             callback : function(){
39859                 this.el.setStyle("z-index", "");
39860                 this.afterSlide();
39861                 if(this.split){
39862                     this.split.el.show();
39863                 }
39864                 this.fireEvent("invalidated", this);
39865                 this.fireEvent("expanded", this);
39866             },
39867             scope: this,
39868             block: true
39869         });
39870     },
39871
39872     anchors : {
39873         "west" : "left",
39874         "east" : "right",
39875         "north" : "top",
39876         "south" : "bottom"
39877     },
39878
39879     sanchors : {
39880         "west" : "l",
39881         "east" : "r",
39882         "north" : "t",
39883         "south" : "b"
39884     },
39885
39886     canchors : {
39887         "west" : "tl-tr",
39888         "east" : "tr-tl",
39889         "north" : "tl-bl",
39890         "south" : "bl-tl"
39891     },
39892
39893     getAnchor : function(){
39894         return this.anchors[this.position];
39895     },
39896
39897     getCollapseAnchor : function(){
39898         return this.canchors[this.position];
39899     },
39900
39901     getSlideAnchor : function(){
39902         return this.sanchors[this.position];
39903     },
39904
39905     getAlignAdj : function(){
39906         var cm = this.cmargins;
39907         switch(this.position){
39908             case "west":
39909                 return [0, 0];
39910             break;
39911             case "east":
39912                 return [0, 0];
39913             break;
39914             case "north":
39915                 return [0, 0];
39916             break;
39917             case "south":
39918                 return [0, 0];
39919             break;
39920         }
39921     },
39922
39923     getExpandAdj : function(){
39924         var c = this.collapsedEl, cm = this.cmargins;
39925         switch(this.position){
39926             case "west":
39927                 return [-(cm.right+c.getWidth()+cm.left), 0];
39928             break;
39929             case "east":
39930                 return [cm.right+c.getWidth()+cm.left, 0];
39931             break;
39932             case "north":
39933                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39934             break;
39935             case "south":
39936                 return [0, cm.top+cm.bottom+c.getHeight()];
39937             break;
39938         }
39939     }
39940 });/*
39941  * Based on:
39942  * Ext JS Library 1.1.1
39943  * Copyright(c) 2006-2007, Ext JS, LLC.
39944  *
39945  * Originally Released Under LGPL - original licence link has changed is not relivant.
39946  *
39947  * Fork - LGPL
39948  * <script type="text/javascript">
39949  */
39950 /*
39951  * These classes are private internal classes
39952  */
39953 Roo.bootstrap.layout.Center = function(config){
39954     config.region = "center";
39955     Roo.bootstrap.layout.Region.call(this, config);
39956     this.visible = true;
39957     this.minWidth = config.minWidth || 20;
39958     this.minHeight = config.minHeight || 20;
39959 };
39960
39961 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39962     hide : function(){
39963         // center panel can't be hidden
39964     },
39965     
39966     show : function(){
39967         // center panel can't be hidden
39968     },
39969     
39970     getMinWidth: function(){
39971         return this.minWidth;
39972     },
39973     
39974     getMinHeight: function(){
39975         return this.minHeight;
39976     }
39977 });
39978
39979
39980
39981
39982  
39983
39984
39985
39986
39987
39988
39989 Roo.bootstrap.layout.North = function(config)
39990 {
39991     config.region = 'north';
39992     config.cursor = 'n-resize';
39993     
39994     Roo.bootstrap.layout.Split.call(this, config);
39995     
39996     
39997     if(this.split){
39998         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39999         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40000         this.split.el.addClass("roo-layout-split-v");
40001     }
40002     //var size = config.initialSize || config.height;
40003     //if(this.el && typeof size != "undefined"){
40004     //    this.el.setHeight(size);
40005     //}
40006 };
40007 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40008 {
40009     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40010      
40011      
40012     onRender : function(ctr, pos)
40013     {
40014         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40015         var size = this.config.initialSize || this.config.height;
40016         if(this.el && typeof size != "undefined"){
40017             this.el.setHeight(size);
40018         }
40019     
40020     },
40021     
40022     getBox : function(){
40023         if(this.collapsed){
40024             return this.collapsedEl.getBox();
40025         }
40026         var box = this.el.getBox();
40027         if(this.split){
40028             box.height += this.split.el.getHeight();
40029         }
40030         return box;
40031     },
40032     
40033     updateBox : function(box){
40034         if(this.split && !this.collapsed){
40035             box.height -= this.split.el.getHeight();
40036             this.split.el.setLeft(box.x);
40037             this.split.el.setTop(box.y+box.height);
40038             this.split.el.setWidth(box.width);
40039         }
40040         if(this.collapsed){
40041             this.updateBody(box.width, null);
40042         }
40043         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40044     }
40045 });
40046
40047
40048
40049
40050
40051 Roo.bootstrap.layout.South = function(config){
40052     config.region = 'south';
40053     config.cursor = 's-resize';
40054     Roo.bootstrap.layout.Split.call(this, config);
40055     if(this.split){
40056         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40057         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40058         this.split.el.addClass("roo-layout-split-v");
40059     }
40060     
40061 };
40062
40063 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40064     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40065     
40066     onRender : function(ctr, pos)
40067     {
40068         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40069         var size = this.config.initialSize || this.config.height;
40070         if(this.el && typeof size != "undefined"){
40071             this.el.setHeight(size);
40072         }
40073     
40074     },
40075     
40076     getBox : function(){
40077         if(this.collapsed){
40078             return this.collapsedEl.getBox();
40079         }
40080         var box = this.el.getBox();
40081         if(this.split){
40082             var sh = this.split.el.getHeight();
40083             box.height += sh;
40084             box.y -= sh;
40085         }
40086         return box;
40087     },
40088     
40089     updateBox : function(box){
40090         if(this.split && !this.collapsed){
40091             var sh = this.split.el.getHeight();
40092             box.height -= sh;
40093             box.y += sh;
40094             this.split.el.setLeft(box.x);
40095             this.split.el.setTop(box.y-sh);
40096             this.split.el.setWidth(box.width);
40097         }
40098         if(this.collapsed){
40099             this.updateBody(box.width, null);
40100         }
40101         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40102     }
40103 });
40104
40105 Roo.bootstrap.layout.East = function(config){
40106     config.region = "east";
40107     config.cursor = "e-resize";
40108     Roo.bootstrap.layout.Split.call(this, config);
40109     if(this.split){
40110         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40111         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40112         this.split.el.addClass("roo-layout-split-h");
40113     }
40114     
40115 };
40116 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40117     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40118     
40119     onRender : function(ctr, pos)
40120     {
40121         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40122         var size = this.config.initialSize || this.config.width;
40123         if(this.el && typeof size != "undefined"){
40124             this.el.setWidth(size);
40125         }
40126     
40127     },
40128     
40129     getBox : function(){
40130         if(this.collapsed){
40131             return this.collapsedEl.getBox();
40132         }
40133         var box = this.el.getBox();
40134         if(this.split){
40135             var sw = this.split.el.getWidth();
40136             box.width += sw;
40137             box.x -= sw;
40138         }
40139         return box;
40140     },
40141
40142     updateBox : function(box){
40143         if(this.split && !this.collapsed){
40144             var sw = this.split.el.getWidth();
40145             box.width -= sw;
40146             this.split.el.setLeft(box.x);
40147             this.split.el.setTop(box.y);
40148             this.split.el.setHeight(box.height);
40149             box.x += sw;
40150         }
40151         if(this.collapsed){
40152             this.updateBody(null, box.height);
40153         }
40154         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40155     }
40156 });
40157
40158 Roo.bootstrap.layout.West = function(config){
40159     config.region = "west";
40160     config.cursor = "w-resize";
40161     
40162     Roo.bootstrap.layout.Split.call(this, config);
40163     if(this.split){
40164         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40165         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40166         this.split.el.addClass("roo-layout-split-h");
40167     }
40168     
40169 };
40170 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40171     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40172     
40173     onRender: function(ctr, pos)
40174     {
40175         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40176         var size = this.config.initialSize || this.config.width;
40177         if(typeof size != "undefined"){
40178             this.el.setWidth(size);
40179         }
40180     },
40181     
40182     getBox : function(){
40183         if(this.collapsed){
40184             return this.collapsedEl.getBox();
40185         }
40186         var box = this.el.getBox();
40187         if (box.width == 0) {
40188             box.width = this.config.width; // kludge?
40189         }
40190         if(this.split){
40191             box.width += this.split.el.getWidth();
40192         }
40193         return box;
40194     },
40195     
40196     updateBox : function(box){
40197         if(this.split && !this.collapsed){
40198             var sw = this.split.el.getWidth();
40199             box.width -= sw;
40200             this.split.el.setLeft(box.x+box.width);
40201             this.split.el.setTop(box.y);
40202             this.split.el.setHeight(box.height);
40203         }
40204         if(this.collapsed){
40205             this.updateBody(null, box.height);
40206         }
40207         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40208     }
40209 });Roo.namespace("Roo.bootstrap.panel");/*
40210  * Based on:
40211  * Ext JS Library 1.1.1
40212  * Copyright(c) 2006-2007, Ext JS, LLC.
40213  *
40214  * Originally Released Under LGPL - original licence link has changed is not relivant.
40215  *
40216  * Fork - LGPL
40217  * <script type="text/javascript">
40218  */
40219 /**
40220  * @class Roo.bootstrap.paenl.Content
40221  * @extends Roo.util.Observable
40222  * @builder-top
40223  * @children Roo.bootstrap.Component
40224  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40225  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40226  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40227  * @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
40228  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40229  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40230  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40231  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40232  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40233  * @cfg {String} title          The title for this panel
40234  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40235  * @cfg {String} url            Calls {@link #setUrl} with this value
40236  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40237  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40238  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40239  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40240  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40241  * @cfg {Boolean} badges render the badges
40242  * @cfg {String} cls  extra classes to use  
40243  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40244  
40245  * @constructor
40246  * Create a new ContentPanel.
40247  * @param {String/Object} config A string to set only the title or a config object
40248  
40249  */
40250 Roo.bootstrap.panel.Content = function( config){
40251     
40252     this.tpl = config.tpl || false;
40253     
40254     var el = config.el;
40255     var content = config.content;
40256
40257     if(config.autoCreate){ // xtype is available if this is called from factory
40258         el = Roo.id();
40259     }
40260     this.el = Roo.get(el);
40261     if(!this.el && config && config.autoCreate){
40262         if(typeof config.autoCreate == "object"){
40263             if(!config.autoCreate.id){
40264                 config.autoCreate.id = config.id||el;
40265             }
40266             this.el = Roo.DomHelper.append(document.body,
40267                         config.autoCreate, true);
40268         }else{
40269             var elcfg =  {
40270                 tag: "div",
40271                 cls: (config.cls || '') +
40272                     (config.background ? ' bg-' + config.background : '') +
40273                     " roo-layout-inactive-content",
40274                 id: config.id||el
40275             };
40276             if (config.iframe) {
40277                 elcfg.cn = [
40278                     {
40279                         tag : 'iframe',
40280                         style : 'border: 0px',
40281                         src : 'about:blank'
40282                     }
40283                 ];
40284             }
40285               
40286             if (config.html) {
40287                 elcfg.html = config.html;
40288                 
40289             }
40290                         
40291             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40292             if (config.iframe) {
40293                 this.iframeEl = this.el.select('iframe',true).first();
40294             }
40295             
40296         }
40297     } 
40298     this.closable = false;
40299     this.loaded = false;
40300     this.active = false;
40301    
40302       
40303     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40304         
40305         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40306         
40307         this.wrapEl = this.el; //this.el.wrap();
40308         var ti = [];
40309         if (config.toolbar.items) {
40310             ti = config.toolbar.items ;
40311             delete config.toolbar.items ;
40312         }
40313         
40314         var nitems = [];
40315         this.toolbar.render(this.wrapEl, 'before');
40316         for(var i =0;i < ti.length;i++) {
40317           //  Roo.log(['add child', items[i]]);
40318             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40319         }
40320         this.toolbar.items = nitems;
40321         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40322         delete config.toolbar;
40323         
40324     }
40325     /*
40326     // xtype created footer. - not sure if will work as we normally have to render first..
40327     if (this.footer && !this.footer.el && this.footer.xtype) {
40328         if (!this.wrapEl) {
40329             this.wrapEl = this.el.wrap();
40330         }
40331     
40332         this.footer.container = this.wrapEl.createChild();
40333          
40334         this.footer = Roo.factory(this.footer, Roo);
40335         
40336     }
40337     */
40338     
40339      if(typeof config == "string"){
40340         this.title = config;
40341     }else{
40342         Roo.apply(this, config);
40343     }
40344     
40345     if(this.resizeEl){
40346         this.resizeEl = Roo.get(this.resizeEl, true);
40347     }else{
40348         this.resizeEl = this.el;
40349     }
40350     // handle view.xtype
40351     
40352  
40353     
40354     
40355     this.addEvents({
40356         /**
40357          * @event activate
40358          * Fires when this panel is activated. 
40359          * @param {Roo.ContentPanel} this
40360          */
40361         "activate" : true,
40362         /**
40363          * @event deactivate
40364          * Fires when this panel is activated. 
40365          * @param {Roo.ContentPanel} this
40366          */
40367         "deactivate" : true,
40368
40369         /**
40370          * @event resize
40371          * Fires when this panel is resized if fitToFrame is true.
40372          * @param {Roo.ContentPanel} this
40373          * @param {Number} width The width after any component adjustments
40374          * @param {Number} height The height after any component adjustments
40375          */
40376         "resize" : true,
40377         
40378          /**
40379          * @event render
40380          * Fires when this tab is created
40381          * @param {Roo.ContentPanel} this
40382          */
40383         "render" : true,
40384         
40385           /**
40386          * @event scroll
40387          * Fires when this content is scrolled
40388          * @param {Roo.ContentPanel} this
40389          * @param {Event} scrollEvent
40390          */
40391         "scroll" : true
40392         
40393         
40394         
40395     });
40396     
40397
40398     
40399     
40400     if(this.autoScroll && !this.iframe){
40401         this.resizeEl.setStyle("overflow", "auto");
40402         this.resizeEl.on('scroll', this.onScroll, this);
40403     } else {
40404         // fix randome scrolling
40405         //this.el.on('scroll', function() {
40406         //    Roo.log('fix random scolling');
40407         //    this.scrollTo('top',0); 
40408         //});
40409     }
40410     content = content || this.content;
40411     if(content){
40412         this.setContent(content);
40413     }
40414     if(config && config.url){
40415         this.setUrl(this.url, this.params, this.loadOnce);
40416     }
40417     
40418     
40419     
40420     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40421     
40422     if (this.view && typeof(this.view.xtype) != 'undefined') {
40423         this.view.el = this.el.appendChild(document.createElement("div"));
40424         this.view = Roo.factory(this.view); 
40425         this.view.render  &&  this.view.render(false, '');  
40426     }
40427     
40428     
40429     this.fireEvent('render', this);
40430 };
40431
40432 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40433     
40434     cls : '',
40435     background : '',
40436     
40437     tabTip : '',
40438     
40439     iframe : false,
40440     iframeEl : false,
40441     
40442     /* Resize Element - use this to work out scroll etc. */
40443     resizeEl : false,
40444     
40445     setRegion : function(region){
40446         this.region = region;
40447         this.setActiveClass(region && !this.background);
40448     },
40449     
40450     
40451     setActiveClass: function(state)
40452     {
40453         if(state){
40454            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40455            this.el.setStyle('position','relative');
40456         }else{
40457            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40458            this.el.setStyle('position', 'absolute');
40459         } 
40460     },
40461     
40462     /**
40463      * Returns the toolbar for this Panel if one was configured. 
40464      * @return {Roo.Toolbar} 
40465      */
40466     getToolbar : function(){
40467         return this.toolbar;
40468     },
40469     
40470     setActiveState : function(active)
40471     {
40472         this.active = active;
40473         this.setActiveClass(active);
40474         if(!active){
40475             if(this.fireEvent("deactivate", this) === false){
40476                 return false;
40477             }
40478             return true;
40479         }
40480         this.fireEvent("activate", this);
40481         return true;
40482     },
40483     /**
40484      * Updates this panel's element (not for iframe)
40485      * @param {String} content The new content
40486      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40487     */
40488     setContent : function(content, loadScripts){
40489         if (this.iframe) {
40490             return;
40491         }
40492         
40493         this.el.update(content, loadScripts);
40494     },
40495
40496     ignoreResize : function(w, h){
40497         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40498             return true;
40499         }else{
40500             this.lastSize = {width: w, height: h};
40501             return false;
40502         }
40503     },
40504     /**
40505      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40506      * @return {Roo.UpdateManager} The UpdateManager
40507      */
40508     getUpdateManager : function(){
40509         if (this.iframe) {
40510             return false;
40511         }
40512         return this.el.getUpdateManager();
40513     },
40514      /**
40515      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40516      * Does not work with IFRAME contents
40517      * @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:
40518 <pre><code>
40519 panel.load({
40520     url: "your-url.php",
40521     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40522     callback: yourFunction,
40523     scope: yourObject, //(optional scope)
40524     discardUrl: false,
40525     nocache: false,
40526     text: "Loading...",
40527     timeout: 30,
40528     scripts: false
40529 });
40530 </code></pre>
40531      
40532      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40533      * 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.
40534      * @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}
40535      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40536      * @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.
40537      * @return {Roo.ContentPanel} this
40538      */
40539     load : function(){
40540         
40541         if (this.iframe) {
40542             return this;
40543         }
40544         
40545         var um = this.el.getUpdateManager();
40546         um.update.apply(um, arguments);
40547         return this;
40548     },
40549
40550
40551     /**
40552      * 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.
40553      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40554      * @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)
40555      * @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)
40556      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40557      */
40558     setUrl : function(url, params, loadOnce){
40559         if (this.iframe) {
40560             this.iframeEl.dom.src = url;
40561             return false;
40562         }
40563         
40564         if(this.refreshDelegate){
40565             this.removeListener("activate", this.refreshDelegate);
40566         }
40567         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40568         this.on("activate", this.refreshDelegate);
40569         return this.el.getUpdateManager();
40570     },
40571     
40572     _handleRefresh : function(url, params, loadOnce){
40573         if(!loadOnce || !this.loaded){
40574             var updater = this.el.getUpdateManager();
40575             updater.update(url, params, this._setLoaded.createDelegate(this));
40576         }
40577     },
40578     
40579     _setLoaded : function(){
40580         this.loaded = true;
40581     }, 
40582     
40583     /**
40584      * Returns this panel's id
40585      * @return {String} 
40586      */
40587     getId : function(){
40588         return this.el.id;
40589     },
40590     
40591     /** 
40592      * Returns this panel's element - used by regiosn to add.
40593      * @return {Roo.Element} 
40594      */
40595     getEl : function(){
40596         return this.wrapEl || this.el;
40597     },
40598     
40599    
40600     
40601     adjustForComponents : function(width, height)
40602     {
40603         //Roo.log('adjustForComponents ');
40604         if(this.resizeEl != this.el){
40605             width -= this.el.getFrameWidth('lr');
40606             height -= this.el.getFrameWidth('tb');
40607         }
40608         if(this.toolbar){
40609             var te = this.toolbar.getEl();
40610             te.setWidth(width);
40611             height -= te.getHeight();
40612         }
40613         if(this.footer){
40614             var te = this.footer.getEl();
40615             te.setWidth(width);
40616             height -= te.getHeight();
40617         }
40618         
40619         
40620         if(this.adjustments){
40621             width += this.adjustments[0];
40622             height += this.adjustments[1];
40623         }
40624         return {"width": width, "height": height};
40625     },
40626     
40627     setSize : function(width, height){
40628         if(this.fitToFrame && !this.ignoreResize(width, height)){
40629             if(this.fitContainer && this.resizeEl != this.el){
40630                 this.el.setSize(width, height);
40631             }
40632             var size = this.adjustForComponents(width, height);
40633             if (this.iframe) {
40634                 this.iframeEl.setSize(width,height);
40635             }
40636             
40637             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40638             this.fireEvent('resize', this, size.width, size.height);
40639             
40640             
40641         }
40642     },
40643     
40644     /**
40645      * Returns this panel's title
40646      * @return {String} 
40647      */
40648     getTitle : function(){
40649         
40650         if (typeof(this.title) != 'object') {
40651             return this.title;
40652         }
40653         
40654         var t = '';
40655         for (var k in this.title) {
40656             if (!this.title.hasOwnProperty(k)) {
40657                 continue;
40658             }
40659             
40660             if (k.indexOf('-') >= 0) {
40661                 var s = k.split('-');
40662                 for (var i = 0; i<s.length; i++) {
40663                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40664                 }
40665             } else {
40666                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40667             }
40668         }
40669         return t;
40670     },
40671     
40672     /**
40673      * Set this panel's title
40674      * @param {String} title
40675      */
40676     setTitle : function(title){
40677         this.title = title;
40678         if(this.region){
40679             this.region.updatePanelTitle(this, title);
40680         }
40681     },
40682     
40683     /**
40684      * Returns true is this panel was configured to be closable
40685      * @return {Boolean} 
40686      */
40687     isClosable : function(){
40688         return this.closable;
40689     },
40690     
40691     beforeSlide : function(){
40692         this.el.clip();
40693         this.resizeEl.clip();
40694     },
40695     
40696     afterSlide : function(){
40697         this.el.unclip();
40698         this.resizeEl.unclip();
40699     },
40700     
40701     /**
40702      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40703      *   Will fail silently if the {@link #setUrl} method has not been called.
40704      *   This does not activate the panel, just updates its content.
40705      */
40706     refresh : function(){
40707         if(this.refreshDelegate){
40708            this.loaded = false;
40709            this.refreshDelegate();
40710         }
40711     },
40712     
40713     /**
40714      * Destroys this panel
40715      */
40716     destroy : function(){
40717         this.el.removeAllListeners();
40718         var tempEl = document.createElement("span");
40719         tempEl.appendChild(this.el.dom);
40720         tempEl.innerHTML = "";
40721         this.el.remove();
40722         this.el = null;
40723     },
40724     
40725     /**
40726      * form - if the content panel contains a form - this is a reference to it.
40727      * @type {Roo.form.Form}
40728      */
40729     form : false,
40730     /**
40731      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40732      *    This contains a reference to it.
40733      * @type {Roo.View}
40734      */
40735     view : false,
40736     
40737       /**
40738      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40739      * <pre><code>
40740
40741 layout.addxtype({
40742        xtype : 'Form',
40743        items: [ .... ]
40744    }
40745 );
40746
40747 </code></pre>
40748      * @param {Object} cfg Xtype definition of item to add.
40749      */
40750     
40751     
40752     getChildContainer: function () {
40753         return this.getEl();
40754     },
40755     
40756     
40757     onScroll : function(e)
40758     {
40759         this.fireEvent('scroll', this, e);
40760     }
40761     
40762     
40763     /*
40764         var  ret = new Roo.factory(cfg);
40765         return ret;
40766         
40767         
40768         // add form..
40769         if (cfg.xtype.match(/^Form$/)) {
40770             
40771             var el;
40772             //if (this.footer) {
40773             //    el = this.footer.container.insertSibling(false, 'before');
40774             //} else {
40775                 el = this.el.createChild();
40776             //}
40777
40778             this.form = new  Roo.form.Form(cfg);
40779             
40780             
40781             if ( this.form.allItems.length) {
40782                 this.form.render(el.dom);
40783             }
40784             return this.form;
40785         }
40786         // should only have one of theses..
40787         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40788             // views.. should not be just added - used named prop 'view''
40789             
40790             cfg.el = this.el.appendChild(document.createElement("div"));
40791             // factory?
40792             
40793             var ret = new Roo.factory(cfg);
40794              
40795              ret.render && ret.render(false, ''); // render blank..
40796             this.view = ret;
40797             return ret;
40798         }
40799         return false;
40800     }
40801     \*/
40802 });
40803  
40804 /**
40805  * @class Roo.bootstrap.panel.Grid
40806  * @extends Roo.bootstrap.panel.Content
40807  * @constructor
40808  * Create a new GridPanel.
40809  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40810  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40811  * @param {Object} config A the config object
40812   
40813  */
40814
40815
40816
40817 Roo.bootstrap.panel.Grid = function(config)
40818 {
40819     
40820       
40821     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40822         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40823
40824     config.el = this.wrapper;
40825     //this.el = this.wrapper;
40826     
40827       if (config.container) {
40828         // ctor'ed from a Border/panel.grid
40829         
40830         
40831         this.wrapper.setStyle("overflow", "hidden");
40832         this.wrapper.addClass('roo-grid-container');
40833
40834     }
40835     
40836     
40837     if(config.toolbar){
40838         var tool_el = this.wrapper.createChild();    
40839         this.toolbar = Roo.factory(config.toolbar);
40840         var ti = [];
40841         if (config.toolbar.items) {
40842             ti = config.toolbar.items ;
40843             delete config.toolbar.items ;
40844         }
40845         
40846         var nitems = [];
40847         this.toolbar.render(tool_el);
40848         for(var i =0;i < ti.length;i++) {
40849           //  Roo.log(['add child', items[i]]);
40850             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40851         }
40852         this.toolbar.items = nitems;
40853         
40854         delete config.toolbar;
40855     }
40856     
40857     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40858     config.grid.scrollBody = true;;
40859     config.grid.monitorWindowResize = false; // turn off autosizing
40860     config.grid.autoHeight = false;
40861     config.grid.autoWidth = false;
40862     
40863     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40864     
40865     if (config.background) {
40866         // render grid on panel activation (if panel background)
40867         this.on('activate', function(gp) {
40868             if (!gp.grid.rendered) {
40869                 gp.grid.render(this.wrapper);
40870                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40871             }
40872         });
40873             
40874     } else {
40875         this.grid.render(this.wrapper);
40876         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40877
40878     }
40879     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40880     // ??? needed ??? config.el = this.wrapper;
40881     
40882     
40883     
40884   
40885     // xtype created footer. - not sure if will work as we normally have to render first..
40886     if (this.footer && !this.footer.el && this.footer.xtype) {
40887         
40888         var ctr = this.grid.getView().getFooterPanel(true);
40889         this.footer.dataSource = this.grid.dataSource;
40890         this.footer = Roo.factory(this.footer, Roo);
40891         this.footer.render(ctr);
40892         
40893     }
40894     
40895     
40896     
40897     
40898      
40899 };
40900
40901 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40902     getId : function(){
40903         return this.grid.id;
40904     },
40905     
40906     /**
40907      * Returns the grid for this panel
40908      * @return {Roo.bootstrap.Table} 
40909      */
40910     getGrid : function(){
40911         return this.grid;    
40912     },
40913     
40914     setSize : function(width, height){
40915         if(!this.ignoreResize(width, height)){
40916             var grid = this.grid;
40917             var size = this.adjustForComponents(width, height);
40918             // tfoot is not a footer?
40919           
40920             
40921             var gridel = grid.getGridEl();
40922             gridel.setSize(size.width, size.height);
40923             
40924             var tbd = grid.getGridEl().select('tbody', true).first();
40925             var thd = grid.getGridEl().select('thead',true).first();
40926             var tbf= grid.getGridEl().select('tfoot', true).first();
40927
40928             if (tbf) {
40929                 size.height -= tbf.getHeight();
40930             }
40931             if (thd) {
40932                 size.height -= thd.getHeight();
40933             }
40934             
40935             tbd.setSize(size.width, size.height );
40936             // this is for the account management tab -seems to work there.
40937             var thd = grid.getGridEl().select('thead',true).first();
40938             //if (tbd) {
40939             //    tbd.setSize(size.width, size.height - thd.getHeight());
40940             //}
40941              
40942             grid.autoSize();
40943         }
40944     },
40945      
40946     
40947     
40948     beforeSlide : function(){
40949         this.grid.getView().scroller.clip();
40950     },
40951     
40952     afterSlide : function(){
40953         this.grid.getView().scroller.unclip();
40954     },
40955     
40956     destroy : function(){
40957         this.grid.destroy();
40958         delete this.grid;
40959         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40960     }
40961 });
40962
40963 /**
40964  * @class Roo.bootstrap.panel.Nest
40965  * @extends Roo.bootstrap.panel.Content
40966  * @constructor
40967  * Create a new Panel, that can contain a layout.Border.
40968  * 
40969  * 
40970  * @param {String/Object} config A string to set only the title or a config object
40971  */
40972 Roo.bootstrap.panel.Nest = function(config)
40973 {
40974     // construct with only one argument..
40975     /* FIXME - implement nicer consturctors
40976     if (layout.layout) {
40977         config = layout;
40978         layout = config.layout;
40979         delete config.layout;
40980     }
40981     if (layout.xtype && !layout.getEl) {
40982         // then layout needs constructing..
40983         layout = Roo.factory(layout, Roo);
40984     }
40985     */
40986     
40987     config.el =  config.layout.getEl();
40988     
40989     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40990     
40991     config.layout.monitorWindowResize = false; // turn off autosizing
40992     this.layout = config.layout;
40993     this.layout.getEl().addClass("roo-layout-nested-layout");
40994     this.layout.parent = this;
40995     
40996     
40997     
40998     
40999 };
41000
41001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41002     /**
41003     * @cfg {Roo.BorderLayout} layout The layout for this panel
41004     */
41005     layout : false,
41006
41007     setSize : function(width, height){
41008         if(!this.ignoreResize(width, height)){
41009             var size = this.adjustForComponents(width, height);
41010             var el = this.layout.getEl();
41011             if (size.height < 1) {
41012                 el.setWidth(size.width);   
41013             } else {
41014                 el.setSize(size.width, size.height);
41015             }
41016             var touch = el.dom.offsetWidth;
41017             this.layout.layout();
41018             // ie requires a double layout on the first pass
41019             if(Roo.isIE && !this.initialized){
41020                 this.initialized = true;
41021                 this.layout.layout();
41022             }
41023         }
41024     },
41025     
41026     // activate all subpanels if not currently active..
41027     
41028     setActiveState : function(active){
41029         this.active = active;
41030         this.setActiveClass(active);
41031         
41032         if(!active){
41033             this.fireEvent("deactivate", this);
41034             return;
41035         }
41036         
41037         this.fireEvent("activate", this);
41038         // not sure if this should happen before or after..
41039         if (!this.layout) {
41040             return; // should not happen..
41041         }
41042         var reg = false;
41043         for (var r in this.layout.regions) {
41044             reg = this.layout.getRegion(r);
41045             if (reg.getActivePanel()) {
41046                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41047                 reg.setActivePanel(reg.getActivePanel());
41048                 continue;
41049             }
41050             if (!reg.panels.length) {
41051                 continue;
41052             }
41053             reg.showPanel(reg.getPanel(0));
41054         }
41055         
41056         
41057         
41058         
41059     },
41060     
41061     /**
41062      * Returns the nested BorderLayout for this panel
41063      * @return {Roo.BorderLayout} 
41064      */
41065     getLayout : function(){
41066         return this.layout;
41067     },
41068     
41069      /**
41070      * Adds a xtype elements to the layout of the nested panel
41071      * <pre><code>
41072
41073 panel.addxtype({
41074        xtype : 'ContentPanel',
41075        region: 'west',
41076        items: [ .... ]
41077    }
41078 );
41079
41080 panel.addxtype({
41081         xtype : 'NestedLayoutPanel',
41082         region: 'west',
41083         layout: {
41084            center: { },
41085            west: { }   
41086         },
41087         items : [ ... list of content panels or nested layout panels.. ]
41088    }
41089 );
41090 </code></pre>
41091      * @param {Object} cfg Xtype definition of item to add.
41092      */
41093     addxtype : function(cfg) {
41094         return this.layout.addxtype(cfg);
41095     
41096     }
41097 });/*
41098  * Based on:
41099  * Ext JS Library 1.1.1
41100  * Copyright(c) 2006-2007, Ext JS, LLC.
41101  *
41102  * Originally Released Under LGPL - original licence link has changed is not relivant.
41103  *
41104  * Fork - LGPL
41105  * <script type="text/javascript">
41106  */
41107 /**
41108  * @class Roo.TabPanel
41109  * @extends Roo.util.Observable
41110  * A lightweight tab container.
41111  * <br><br>
41112  * Usage:
41113  * <pre><code>
41114 // basic tabs 1, built from existing content
41115 var tabs = new Roo.TabPanel("tabs1");
41116 tabs.addTab("script", "View Script");
41117 tabs.addTab("markup", "View Markup");
41118 tabs.activate("script");
41119
41120 // more advanced tabs, built from javascript
41121 var jtabs = new Roo.TabPanel("jtabs");
41122 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41123
41124 // set up the UpdateManager
41125 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41126 var updater = tab2.getUpdateManager();
41127 updater.setDefaultUrl("ajax1.htm");
41128 tab2.on('activate', updater.refresh, updater, true);
41129
41130 // Use setUrl for Ajax loading
41131 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41132 tab3.setUrl("ajax2.htm", null, true);
41133
41134 // Disabled tab
41135 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41136 tab4.disable();
41137
41138 jtabs.activate("jtabs-1");
41139  * </code></pre>
41140  * @constructor
41141  * Create a new TabPanel.
41142  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41143  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41144  */
41145 Roo.bootstrap.panel.Tabs = function(config){
41146     /**
41147     * The container element for this TabPanel.
41148     * @type Roo.Element
41149     */
41150     this.el = Roo.get(config.el);
41151     delete config.el;
41152     if(config){
41153         if(typeof config == "boolean"){
41154             this.tabPosition = config ? "bottom" : "top";
41155         }else{
41156             Roo.apply(this, config);
41157         }
41158     }
41159     
41160     if(this.tabPosition == "bottom"){
41161         // if tabs are at the bottom = create the body first.
41162         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41163         this.el.addClass("roo-tabs-bottom");
41164     }
41165     // next create the tabs holders
41166     
41167     if (this.tabPosition == "west"){
41168         
41169         var reg = this.region; // fake it..
41170         while (reg) {
41171             if (!reg.mgr.parent) {
41172                 break;
41173             }
41174             reg = reg.mgr.parent.region;
41175         }
41176         Roo.log("got nest?");
41177         Roo.log(reg);
41178         if (reg.mgr.getRegion('west')) {
41179             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41180             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41181             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41182             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41183             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41184         
41185             
41186         }
41187         
41188         
41189     } else {
41190      
41191         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41192         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41193         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41194         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41195     }
41196     
41197     
41198     if(Roo.isIE){
41199         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41200     }
41201     
41202     // finally - if tabs are at the top, then create the body last..
41203     if(this.tabPosition != "bottom"){
41204         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41205          * @type Roo.Element
41206          */
41207         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41208         this.el.addClass("roo-tabs-top");
41209     }
41210     this.items = [];
41211
41212     this.bodyEl.setStyle("position", "relative");
41213
41214     this.active = null;
41215     this.activateDelegate = this.activate.createDelegate(this);
41216
41217     this.addEvents({
41218         /**
41219          * @event tabchange
41220          * Fires when the active tab changes
41221          * @param {Roo.TabPanel} this
41222          * @param {Roo.TabPanelItem} activePanel The new active tab
41223          */
41224         "tabchange": true,
41225         /**
41226          * @event beforetabchange
41227          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41228          * @param {Roo.TabPanel} this
41229          * @param {Object} e Set cancel to true on this object to cancel the tab change
41230          * @param {Roo.TabPanelItem} tab The tab being changed to
41231          */
41232         "beforetabchange" : true
41233     });
41234
41235     Roo.EventManager.onWindowResize(this.onResize, this);
41236     this.cpad = this.el.getPadding("lr");
41237     this.hiddenCount = 0;
41238
41239
41240     // toolbar on the tabbar support...
41241     if (this.toolbar) {
41242         alert("no toolbar support yet");
41243         this.toolbar  = false;
41244         /*
41245         var tcfg = this.toolbar;
41246         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41247         this.toolbar = new Roo.Toolbar(tcfg);
41248         if (Roo.isSafari) {
41249             var tbl = tcfg.container.child('table', true);
41250             tbl.setAttribute('width', '100%');
41251         }
41252         */
41253         
41254     }
41255    
41256
41257
41258     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41259 };
41260
41261 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41262     /*
41263      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41264      */
41265     tabPosition : "top",
41266     /*
41267      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41268      */
41269     currentTabWidth : 0,
41270     /*
41271      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41272      */
41273     minTabWidth : 40,
41274     /*
41275      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41276      */
41277     maxTabWidth : 250,
41278     /*
41279      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41280      */
41281     preferredTabWidth : 175,
41282     /*
41283      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41284      */
41285     resizeTabs : false,
41286     /*
41287      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41288      */
41289     monitorResize : true,
41290     /*
41291      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41292      */
41293     toolbar : false,  // set by caller..
41294     
41295     region : false, /// set by caller
41296     
41297     disableTooltips : true, // not used yet...
41298
41299     /**
41300      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41301      * @param {String} id The id of the div to use <b>or create</b>
41302      * @param {String} text The text for the tab
41303      * @param {String} content (optional) Content to put in the TabPanelItem body
41304      * @param {Boolean} closable (optional) True to create a close icon on the tab
41305      * @return {Roo.TabPanelItem} The created TabPanelItem
41306      */
41307     addTab : function(id, text, content, closable, tpl)
41308     {
41309         var item = new Roo.bootstrap.panel.TabItem({
41310             panel: this,
41311             id : id,
41312             text : text,
41313             closable : closable,
41314             tpl : tpl
41315         });
41316         this.addTabItem(item);
41317         if(content){
41318             item.setContent(content);
41319         }
41320         return item;
41321     },
41322
41323     /**
41324      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41325      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41326      * @return {Roo.TabPanelItem}
41327      */
41328     getTab : function(id){
41329         return this.items[id];
41330     },
41331
41332     /**
41333      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41334      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41335      */
41336     hideTab : function(id){
41337         var t = this.items[id];
41338         if(!t.isHidden()){
41339            t.setHidden(true);
41340            this.hiddenCount++;
41341            this.autoSizeTabs();
41342         }
41343     },
41344
41345     /**
41346      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41347      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41348      */
41349     unhideTab : function(id){
41350         var t = this.items[id];
41351         if(t.isHidden()){
41352            t.setHidden(false);
41353            this.hiddenCount--;
41354            this.autoSizeTabs();
41355         }
41356     },
41357
41358     /**
41359      * Adds an existing {@link Roo.TabPanelItem}.
41360      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41361      */
41362     addTabItem : function(item)
41363     {
41364         this.items[item.id] = item;
41365         this.items.push(item);
41366         this.autoSizeTabs();
41367       //  if(this.resizeTabs){
41368     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41369   //         this.autoSizeTabs();
41370 //        }else{
41371 //            item.autoSize();
41372        // }
41373     },
41374
41375     /**
41376      * Removes a {@link Roo.TabPanelItem}.
41377      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41378      */
41379     removeTab : function(id){
41380         var items = this.items;
41381         var tab = items[id];
41382         if(!tab) { return; }
41383         var index = items.indexOf(tab);
41384         if(this.active == tab && items.length > 1){
41385             var newTab = this.getNextAvailable(index);
41386             if(newTab) {
41387                 newTab.activate();
41388             }
41389         }
41390         this.stripEl.dom.removeChild(tab.pnode.dom);
41391         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41392             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41393         }
41394         items.splice(index, 1);
41395         delete this.items[tab.id];
41396         tab.fireEvent("close", tab);
41397         tab.purgeListeners();
41398         this.autoSizeTabs();
41399     },
41400
41401     getNextAvailable : function(start){
41402         var items = this.items;
41403         var index = start;
41404         // look for a next tab that will slide over to
41405         // replace the one being removed
41406         while(index < items.length){
41407             var item = items[++index];
41408             if(item && !item.isHidden()){
41409                 return item;
41410             }
41411         }
41412         // if one isn't found select the previous tab (on the left)
41413         index = start;
41414         while(index >= 0){
41415             var item = items[--index];
41416             if(item && !item.isHidden()){
41417                 return item;
41418             }
41419         }
41420         return null;
41421     },
41422
41423     /**
41424      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41425      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41426      */
41427     disableTab : function(id){
41428         var tab = this.items[id];
41429         if(tab && this.active != tab){
41430             tab.disable();
41431         }
41432     },
41433
41434     /**
41435      * Enables a {@link Roo.TabPanelItem} that is disabled.
41436      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41437      */
41438     enableTab : function(id){
41439         var tab = this.items[id];
41440         tab.enable();
41441     },
41442
41443     /**
41444      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41445      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41446      * @return {Roo.TabPanelItem} The TabPanelItem.
41447      */
41448     activate : function(id)
41449     {
41450         //Roo.log('activite:'  + id);
41451         
41452         var tab = this.items[id];
41453         if(!tab){
41454             return null;
41455         }
41456         if(tab == this.active || tab.disabled){
41457             return tab;
41458         }
41459         var e = {};
41460         this.fireEvent("beforetabchange", this, e, tab);
41461         if(e.cancel !== true && !tab.disabled){
41462             if(this.active){
41463                 this.active.hide();
41464             }
41465             this.active = this.items[id];
41466             this.active.show();
41467             this.fireEvent("tabchange", this, this.active);
41468         }
41469         return tab;
41470     },
41471
41472     /**
41473      * Gets the active {@link Roo.TabPanelItem}.
41474      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41475      */
41476     getActiveTab : function(){
41477         return this.active;
41478     },
41479
41480     /**
41481      * Updates the tab body element to fit the height of the container element
41482      * for overflow scrolling
41483      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41484      */
41485     syncHeight : function(targetHeight){
41486         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41487         var bm = this.bodyEl.getMargins();
41488         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41489         this.bodyEl.setHeight(newHeight);
41490         return newHeight;
41491     },
41492
41493     onResize : function(){
41494         if(this.monitorResize){
41495             this.autoSizeTabs();
41496         }
41497     },
41498
41499     /**
41500      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41501      */
41502     beginUpdate : function(){
41503         this.updating = true;
41504     },
41505
41506     /**
41507      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41508      */
41509     endUpdate : function(){
41510         this.updating = false;
41511         this.autoSizeTabs();
41512     },
41513
41514     /**
41515      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41516      */
41517     autoSizeTabs : function()
41518     {
41519         var count = this.items.length;
41520         var vcount = count - this.hiddenCount;
41521         
41522         if (vcount < 2) {
41523             this.stripEl.hide();
41524         } else {
41525             this.stripEl.show();
41526         }
41527         
41528         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41529             return;
41530         }
41531         
41532         
41533         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41534         var availWidth = Math.floor(w / vcount);
41535         var b = this.stripBody;
41536         if(b.getWidth() > w){
41537             var tabs = this.items;
41538             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41539             if(availWidth < this.minTabWidth){
41540                 /*if(!this.sleft){    // incomplete scrolling code
41541                     this.createScrollButtons();
41542                 }
41543                 this.showScroll();
41544                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41545             }
41546         }else{
41547             if(this.currentTabWidth < this.preferredTabWidth){
41548                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41549             }
41550         }
41551     },
41552
41553     /**
41554      * Returns the number of tabs in this TabPanel.
41555      * @return {Number}
41556      */
41557      getCount : function(){
41558          return this.items.length;
41559      },
41560
41561     /**
41562      * Resizes all the tabs to the passed width
41563      * @param {Number} The new width
41564      */
41565     setTabWidth : function(width){
41566         this.currentTabWidth = width;
41567         for(var i = 0, len = this.items.length; i < len; i++) {
41568                 if(!this.items[i].isHidden()) {
41569                 this.items[i].setWidth(width);
41570             }
41571         }
41572     },
41573
41574     /**
41575      * Destroys this TabPanel
41576      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41577      */
41578     destroy : function(removeEl){
41579         Roo.EventManager.removeResizeListener(this.onResize, this);
41580         for(var i = 0, len = this.items.length; i < len; i++){
41581             this.items[i].purgeListeners();
41582         }
41583         if(removeEl === true){
41584             this.el.update("");
41585             this.el.remove();
41586         }
41587     },
41588     
41589     createStrip : function(container)
41590     {
41591         var strip = document.createElement("nav");
41592         strip.className = Roo.bootstrap.version == 4 ?
41593             "navbar-light bg-light" : 
41594             "navbar navbar-default"; //"x-tabs-wrap";
41595         container.appendChild(strip);
41596         return strip;
41597     },
41598     
41599     createStripList : function(strip)
41600     {
41601         // div wrapper for retard IE
41602         // returns the "tr" element.
41603         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41604         //'<div class="x-tabs-strip-wrap">'+
41605           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41606           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41607         return strip.firstChild; //.firstChild.firstChild.firstChild;
41608     },
41609     createBody : function(container)
41610     {
41611         var body = document.createElement("div");
41612         Roo.id(body, "tab-body");
41613         //Roo.fly(body).addClass("x-tabs-body");
41614         Roo.fly(body).addClass("tab-content");
41615         container.appendChild(body);
41616         return body;
41617     },
41618     createItemBody :function(bodyEl, id){
41619         var body = Roo.getDom(id);
41620         if(!body){
41621             body = document.createElement("div");
41622             body.id = id;
41623         }
41624         //Roo.fly(body).addClass("x-tabs-item-body");
41625         Roo.fly(body).addClass("tab-pane");
41626          bodyEl.insertBefore(body, bodyEl.firstChild);
41627         return body;
41628     },
41629     /** @private */
41630     createStripElements :  function(stripEl, text, closable, tpl)
41631     {
41632         var td = document.createElement("li"); // was td..
41633         td.className = 'nav-item';
41634         
41635         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41636         
41637         
41638         stripEl.appendChild(td);
41639         /*if(closable){
41640             td.className = "x-tabs-closable";
41641             if(!this.closeTpl){
41642                 this.closeTpl = new Roo.Template(
41643                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41644                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41645                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41646                 );
41647             }
41648             var el = this.closeTpl.overwrite(td, {"text": text});
41649             var close = el.getElementsByTagName("div")[0];
41650             var inner = el.getElementsByTagName("em")[0];
41651             return {"el": el, "close": close, "inner": inner};
41652         } else {
41653         */
41654         // not sure what this is..
41655 //            if(!this.tabTpl){
41656                 //this.tabTpl = new Roo.Template(
41657                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41658                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41659                 //);
41660 //                this.tabTpl = new Roo.Template(
41661 //                   '<a href="#">' +
41662 //                   '<span unselectable="on"' +
41663 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41664 //                            ' >{text}</span></a>'
41665 //                );
41666 //                
41667 //            }
41668
41669
41670             var template = tpl || this.tabTpl || false;
41671             
41672             if(!template){
41673                 template =  new Roo.Template(
41674                         Roo.bootstrap.version == 4 ? 
41675                             (
41676                                 '<a class="nav-link" href="#" unselectable="on"' +
41677                                      (this.disableTooltips ? '' : ' title="{text}"') +
41678                                      ' >{text}</a>'
41679                             ) : (
41680                                 '<a class="nav-link" href="#">' +
41681                                 '<span unselectable="on"' +
41682                                          (this.disableTooltips ? '' : ' title="{text}"') +
41683                                     ' >{text}</span></a>'
41684                             )
41685                 );
41686             }
41687             
41688             switch (typeof(template)) {
41689                 case 'object' :
41690                     break;
41691                 case 'string' :
41692                     template = new Roo.Template(template);
41693                     break;
41694                 default :
41695                     break;
41696             }
41697             
41698             var el = template.overwrite(td, {"text": text});
41699             
41700             var inner = el.getElementsByTagName("span")[0];
41701             
41702             return {"el": el, "inner": inner};
41703             
41704     }
41705         
41706     
41707 });
41708
41709 /**
41710  * @class Roo.TabPanelItem
41711  * @extends Roo.util.Observable
41712  * Represents an individual item (tab plus body) in a TabPanel.
41713  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41714  * @param {String} id The id of this TabPanelItem
41715  * @param {String} text The text for the tab of this TabPanelItem
41716  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41717  */
41718 Roo.bootstrap.panel.TabItem = function(config){
41719     /**
41720      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41721      * @type Roo.TabPanel
41722      */
41723     this.tabPanel = config.panel;
41724     /**
41725      * The id for this TabPanelItem
41726      * @type String
41727      */
41728     this.id = config.id;
41729     /** @private */
41730     this.disabled = false;
41731     /** @private */
41732     this.text = config.text;
41733     /** @private */
41734     this.loaded = false;
41735     this.closable = config.closable;
41736
41737     /**
41738      * The body element for this TabPanelItem.
41739      * @type Roo.Element
41740      */
41741     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41742     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41743     this.bodyEl.setStyle("display", "block");
41744     this.bodyEl.setStyle("zoom", "1");
41745     //this.hideAction();
41746
41747     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41748     /** @private */
41749     this.el = Roo.get(els.el);
41750     this.inner = Roo.get(els.inner, true);
41751      this.textEl = Roo.bootstrap.version == 4 ?
41752         this.el : Roo.get(this.el.dom.firstChild, true);
41753
41754     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41755     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41756
41757     
41758 //    this.el.on("mousedown", this.onTabMouseDown, this);
41759     this.el.on("click", this.onTabClick, this);
41760     /** @private */
41761     if(config.closable){
41762         var c = Roo.get(els.close, true);
41763         c.dom.title = this.closeText;
41764         c.addClassOnOver("close-over");
41765         c.on("click", this.closeClick, this);
41766      }
41767
41768     this.addEvents({
41769          /**
41770          * @event activate
41771          * Fires when this tab becomes the active tab.
41772          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41773          * @param {Roo.TabPanelItem} this
41774          */
41775         "activate": true,
41776         /**
41777          * @event beforeclose
41778          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41779          * @param {Roo.TabPanelItem} this
41780          * @param {Object} e Set cancel to true on this object to cancel the close.
41781          */
41782         "beforeclose": true,
41783         /**
41784          * @event close
41785          * Fires when this tab is closed.
41786          * @param {Roo.TabPanelItem} this
41787          */
41788          "close": true,
41789         /**
41790          * @event deactivate
41791          * Fires when this tab is no longer the active tab.
41792          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41793          * @param {Roo.TabPanelItem} this
41794          */
41795          "deactivate" : true
41796     });
41797     this.hidden = false;
41798
41799     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41800 };
41801
41802 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41803            {
41804     purgeListeners : function(){
41805        Roo.util.Observable.prototype.purgeListeners.call(this);
41806        this.el.removeAllListeners();
41807     },
41808     /**
41809      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41810      */
41811     show : function(){
41812         this.status_node.addClass("active");
41813         this.showAction();
41814         if(Roo.isOpera){
41815             this.tabPanel.stripWrap.repaint();
41816         }
41817         this.fireEvent("activate", this.tabPanel, this);
41818     },
41819
41820     /**
41821      * Returns true if this tab is the active tab.
41822      * @return {Boolean}
41823      */
41824     isActive : function(){
41825         return this.tabPanel.getActiveTab() == this;
41826     },
41827
41828     /**
41829      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41830      */
41831     hide : function(){
41832         this.status_node.removeClass("active");
41833         this.hideAction();
41834         this.fireEvent("deactivate", this.tabPanel, this);
41835     },
41836
41837     hideAction : function(){
41838         this.bodyEl.hide();
41839         this.bodyEl.setStyle("position", "absolute");
41840         this.bodyEl.setLeft("-20000px");
41841         this.bodyEl.setTop("-20000px");
41842     },
41843
41844     showAction : function(){
41845         this.bodyEl.setStyle("position", "relative");
41846         this.bodyEl.setTop("");
41847         this.bodyEl.setLeft("");
41848         this.bodyEl.show();
41849     },
41850
41851     /**
41852      * Set the tooltip for the tab.
41853      * @param {String} tooltip The tab's tooltip
41854      */
41855     setTooltip : function(text){
41856         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41857             this.textEl.dom.qtip = text;
41858             this.textEl.dom.removeAttribute('title');
41859         }else{
41860             this.textEl.dom.title = text;
41861         }
41862     },
41863
41864     onTabClick : function(e){
41865         e.preventDefault();
41866         this.tabPanel.activate(this.id);
41867     },
41868
41869     onTabMouseDown : function(e){
41870         e.preventDefault();
41871         this.tabPanel.activate(this.id);
41872     },
41873 /*
41874     getWidth : function(){
41875         return this.inner.getWidth();
41876     },
41877
41878     setWidth : function(width){
41879         var iwidth = width - this.linode.getPadding("lr");
41880         this.inner.setWidth(iwidth);
41881         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41882         this.linode.setWidth(width);
41883     },
41884 */
41885     /**
41886      * Show or hide the tab
41887      * @param {Boolean} hidden True to hide or false to show.
41888      */
41889     setHidden : function(hidden){
41890         this.hidden = hidden;
41891         this.linode.setStyle("display", hidden ? "none" : "");
41892     },
41893
41894     /**
41895      * Returns true if this tab is "hidden"
41896      * @return {Boolean}
41897      */
41898     isHidden : function(){
41899         return this.hidden;
41900     },
41901
41902     /**
41903      * Returns the text for this tab
41904      * @return {String}
41905      */
41906     getText : function(){
41907         return this.text;
41908     },
41909     /*
41910     autoSize : function(){
41911         //this.el.beginMeasure();
41912         this.textEl.setWidth(1);
41913         /*
41914          *  #2804 [new] Tabs in Roojs
41915          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41916          */
41917         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41918         //this.el.endMeasure();
41919     //},
41920
41921     /**
41922      * Sets the text for the tab (Note: this also sets the tooltip text)
41923      * @param {String} text The tab's text and tooltip
41924      */
41925     setText : function(text){
41926         this.text = text;
41927         this.textEl.update(text);
41928         this.setTooltip(text);
41929         //if(!this.tabPanel.resizeTabs){
41930         //    this.autoSize();
41931         //}
41932     },
41933     /**
41934      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41935      */
41936     activate : function(){
41937         this.tabPanel.activate(this.id);
41938     },
41939
41940     /**
41941      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41942      */
41943     disable : function(){
41944         if(this.tabPanel.active != this){
41945             this.disabled = true;
41946             this.status_node.addClass("disabled");
41947         }
41948     },
41949
41950     /**
41951      * Enables this TabPanelItem if it was previously disabled.
41952      */
41953     enable : function(){
41954         this.disabled = false;
41955         this.status_node.removeClass("disabled");
41956     },
41957
41958     /**
41959      * Sets the content for this TabPanelItem.
41960      * @param {String} content The content
41961      * @param {Boolean} loadScripts true to look for and load scripts
41962      */
41963     setContent : function(content, loadScripts){
41964         this.bodyEl.update(content, loadScripts);
41965     },
41966
41967     /**
41968      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41969      * @return {Roo.UpdateManager} The UpdateManager
41970      */
41971     getUpdateManager : function(){
41972         return this.bodyEl.getUpdateManager();
41973     },
41974
41975     /**
41976      * Set a URL to be used to load the content for this TabPanelItem.
41977      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41978      * @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)
41979      * @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)
41980      * @return {Roo.UpdateManager} The UpdateManager
41981      */
41982     setUrl : function(url, params, loadOnce){
41983         if(this.refreshDelegate){
41984             this.un('activate', this.refreshDelegate);
41985         }
41986         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41987         this.on("activate", this.refreshDelegate);
41988         return this.bodyEl.getUpdateManager();
41989     },
41990
41991     /** @private */
41992     _handleRefresh : function(url, params, loadOnce){
41993         if(!loadOnce || !this.loaded){
41994             var updater = this.bodyEl.getUpdateManager();
41995             updater.update(url, params, this._setLoaded.createDelegate(this));
41996         }
41997     },
41998
41999     /**
42000      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42001      *   Will fail silently if the setUrl method has not been called.
42002      *   This does not activate the panel, just updates its content.
42003      */
42004     refresh : function(){
42005         if(this.refreshDelegate){
42006            this.loaded = false;
42007            this.refreshDelegate();
42008         }
42009     },
42010
42011     /** @private */
42012     _setLoaded : function(){
42013         this.loaded = true;
42014     },
42015
42016     /** @private */
42017     closeClick : function(e){
42018         var o = {};
42019         e.stopEvent();
42020         this.fireEvent("beforeclose", this, o);
42021         if(o.cancel !== true){
42022             this.tabPanel.removeTab(this.id);
42023         }
42024     },
42025     /**
42026      * The text displayed in the tooltip for the close icon.
42027      * @type String
42028      */
42029     closeText : "Close this tab"
42030 });
42031 /**
42032 *    This script refer to:
42033 *    Title: International Telephone Input
42034 *    Author: Jack O'Connor
42035 *    Code version:  v12.1.12
42036 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42037 **/
42038
42039 Roo.bootstrap.PhoneInputData = function() {
42040     var d = [
42041       [
42042         "Afghanistan (‫افغانستان‬‎)",
42043         "af",
42044         "93"
42045       ],
42046       [
42047         "Albania (Shqipëri)",
42048         "al",
42049         "355"
42050       ],
42051       [
42052         "Algeria (‫الجزائر‬‎)",
42053         "dz",
42054         "213"
42055       ],
42056       [
42057         "American Samoa",
42058         "as",
42059         "1684"
42060       ],
42061       [
42062         "Andorra",
42063         "ad",
42064         "376"
42065       ],
42066       [
42067         "Angola",
42068         "ao",
42069         "244"
42070       ],
42071       [
42072         "Anguilla",
42073         "ai",
42074         "1264"
42075       ],
42076       [
42077         "Antigua and Barbuda",
42078         "ag",
42079         "1268"
42080       ],
42081       [
42082         "Argentina",
42083         "ar",
42084         "54"
42085       ],
42086       [
42087         "Armenia (Հայաստան)",
42088         "am",
42089         "374"
42090       ],
42091       [
42092         "Aruba",
42093         "aw",
42094         "297"
42095       ],
42096       [
42097         "Australia",
42098         "au",
42099         "61",
42100         0
42101       ],
42102       [
42103         "Austria (Österreich)",
42104         "at",
42105         "43"
42106       ],
42107       [
42108         "Azerbaijan (Azərbaycan)",
42109         "az",
42110         "994"
42111       ],
42112       [
42113         "Bahamas",
42114         "bs",
42115         "1242"
42116       ],
42117       [
42118         "Bahrain (‫البحرين‬‎)",
42119         "bh",
42120         "973"
42121       ],
42122       [
42123         "Bangladesh (বাংলাদেশ)",
42124         "bd",
42125         "880"
42126       ],
42127       [
42128         "Barbados",
42129         "bb",
42130         "1246"
42131       ],
42132       [
42133         "Belarus (Беларусь)",
42134         "by",
42135         "375"
42136       ],
42137       [
42138         "Belgium (België)",
42139         "be",
42140         "32"
42141       ],
42142       [
42143         "Belize",
42144         "bz",
42145         "501"
42146       ],
42147       [
42148         "Benin (Bénin)",
42149         "bj",
42150         "229"
42151       ],
42152       [
42153         "Bermuda",
42154         "bm",
42155         "1441"
42156       ],
42157       [
42158         "Bhutan (འབྲུག)",
42159         "bt",
42160         "975"
42161       ],
42162       [
42163         "Bolivia",
42164         "bo",
42165         "591"
42166       ],
42167       [
42168         "Bosnia and Herzegovina (Босна и Херцеговина)",
42169         "ba",
42170         "387"
42171       ],
42172       [
42173         "Botswana",
42174         "bw",
42175         "267"
42176       ],
42177       [
42178         "Brazil (Brasil)",
42179         "br",
42180         "55"
42181       ],
42182       [
42183         "British Indian Ocean Territory",
42184         "io",
42185         "246"
42186       ],
42187       [
42188         "British Virgin Islands",
42189         "vg",
42190         "1284"
42191       ],
42192       [
42193         "Brunei",
42194         "bn",
42195         "673"
42196       ],
42197       [
42198         "Bulgaria (България)",
42199         "bg",
42200         "359"
42201       ],
42202       [
42203         "Burkina Faso",
42204         "bf",
42205         "226"
42206       ],
42207       [
42208         "Burundi (Uburundi)",
42209         "bi",
42210         "257"
42211       ],
42212       [
42213         "Cambodia (កម្ពុជា)",
42214         "kh",
42215         "855"
42216       ],
42217       [
42218         "Cameroon (Cameroun)",
42219         "cm",
42220         "237"
42221       ],
42222       [
42223         "Canada",
42224         "ca",
42225         "1",
42226         1,
42227         ["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"]
42228       ],
42229       [
42230         "Cape Verde (Kabu Verdi)",
42231         "cv",
42232         "238"
42233       ],
42234       [
42235         "Caribbean Netherlands",
42236         "bq",
42237         "599",
42238         1
42239       ],
42240       [
42241         "Cayman Islands",
42242         "ky",
42243         "1345"
42244       ],
42245       [
42246         "Central African Republic (République centrafricaine)",
42247         "cf",
42248         "236"
42249       ],
42250       [
42251         "Chad (Tchad)",
42252         "td",
42253         "235"
42254       ],
42255       [
42256         "Chile",
42257         "cl",
42258         "56"
42259       ],
42260       [
42261         "China (中国)",
42262         "cn",
42263         "86"
42264       ],
42265       [
42266         "Christmas Island",
42267         "cx",
42268         "61",
42269         2
42270       ],
42271       [
42272         "Cocos (Keeling) Islands",
42273         "cc",
42274         "61",
42275         1
42276       ],
42277       [
42278         "Colombia",
42279         "co",
42280         "57"
42281       ],
42282       [
42283         "Comoros (‫جزر القمر‬‎)",
42284         "km",
42285         "269"
42286       ],
42287       [
42288         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42289         "cd",
42290         "243"
42291       ],
42292       [
42293         "Congo (Republic) (Congo-Brazzaville)",
42294         "cg",
42295         "242"
42296       ],
42297       [
42298         "Cook Islands",
42299         "ck",
42300         "682"
42301       ],
42302       [
42303         "Costa Rica",
42304         "cr",
42305         "506"
42306       ],
42307       [
42308         "Côte d’Ivoire",
42309         "ci",
42310         "225"
42311       ],
42312       [
42313         "Croatia (Hrvatska)",
42314         "hr",
42315         "385"
42316       ],
42317       [
42318         "Cuba",
42319         "cu",
42320         "53"
42321       ],
42322       [
42323         "Curaçao",
42324         "cw",
42325         "599",
42326         0
42327       ],
42328       [
42329         "Cyprus (Κύπρος)",
42330         "cy",
42331         "357"
42332       ],
42333       [
42334         "Czech Republic (Česká republika)",
42335         "cz",
42336         "420"
42337       ],
42338       [
42339         "Denmark (Danmark)",
42340         "dk",
42341         "45"
42342       ],
42343       [
42344         "Djibouti",
42345         "dj",
42346         "253"
42347       ],
42348       [
42349         "Dominica",
42350         "dm",
42351         "1767"
42352       ],
42353       [
42354         "Dominican Republic (República Dominicana)",
42355         "do",
42356         "1",
42357         2,
42358         ["809", "829", "849"]
42359       ],
42360       [
42361         "Ecuador",
42362         "ec",
42363         "593"
42364       ],
42365       [
42366         "Egypt (‫مصر‬‎)",
42367         "eg",
42368         "20"
42369       ],
42370       [
42371         "El Salvador",
42372         "sv",
42373         "503"
42374       ],
42375       [
42376         "Equatorial Guinea (Guinea Ecuatorial)",
42377         "gq",
42378         "240"
42379       ],
42380       [
42381         "Eritrea",
42382         "er",
42383         "291"
42384       ],
42385       [
42386         "Estonia (Eesti)",
42387         "ee",
42388         "372"
42389       ],
42390       [
42391         "Ethiopia",
42392         "et",
42393         "251"
42394       ],
42395       [
42396         "Falkland Islands (Islas Malvinas)",
42397         "fk",
42398         "500"
42399       ],
42400       [
42401         "Faroe Islands (Føroyar)",
42402         "fo",
42403         "298"
42404       ],
42405       [
42406         "Fiji",
42407         "fj",
42408         "679"
42409       ],
42410       [
42411         "Finland (Suomi)",
42412         "fi",
42413         "358",
42414         0
42415       ],
42416       [
42417         "France",
42418         "fr",
42419         "33"
42420       ],
42421       [
42422         "French Guiana (Guyane française)",
42423         "gf",
42424         "594"
42425       ],
42426       [
42427         "French Polynesia (Polynésie française)",
42428         "pf",
42429         "689"
42430       ],
42431       [
42432         "Gabon",
42433         "ga",
42434         "241"
42435       ],
42436       [
42437         "Gambia",
42438         "gm",
42439         "220"
42440       ],
42441       [
42442         "Georgia (საქართველო)",
42443         "ge",
42444         "995"
42445       ],
42446       [
42447         "Germany (Deutschland)",
42448         "de",
42449         "49"
42450       ],
42451       [
42452         "Ghana (Gaana)",
42453         "gh",
42454         "233"
42455       ],
42456       [
42457         "Gibraltar",
42458         "gi",
42459         "350"
42460       ],
42461       [
42462         "Greece (Ελλάδα)",
42463         "gr",
42464         "30"
42465       ],
42466       [
42467         "Greenland (Kalaallit Nunaat)",
42468         "gl",
42469         "299"
42470       ],
42471       [
42472         "Grenada",
42473         "gd",
42474         "1473"
42475       ],
42476       [
42477         "Guadeloupe",
42478         "gp",
42479         "590",
42480         0
42481       ],
42482       [
42483         "Guam",
42484         "gu",
42485         "1671"
42486       ],
42487       [
42488         "Guatemala",
42489         "gt",
42490         "502"
42491       ],
42492       [
42493         "Guernsey",
42494         "gg",
42495         "44",
42496         1
42497       ],
42498       [
42499         "Guinea (Guinée)",
42500         "gn",
42501         "224"
42502       ],
42503       [
42504         "Guinea-Bissau (Guiné Bissau)",
42505         "gw",
42506         "245"
42507       ],
42508       [
42509         "Guyana",
42510         "gy",
42511         "592"
42512       ],
42513       [
42514         "Haiti",
42515         "ht",
42516         "509"
42517       ],
42518       [
42519         "Honduras",
42520         "hn",
42521         "504"
42522       ],
42523       [
42524         "Hong Kong (香港)",
42525         "hk",
42526         "852"
42527       ],
42528       [
42529         "Hungary (Magyarország)",
42530         "hu",
42531         "36"
42532       ],
42533       [
42534         "Iceland (Ísland)",
42535         "is",
42536         "354"
42537       ],
42538       [
42539         "India (भारत)",
42540         "in",
42541         "91"
42542       ],
42543       [
42544         "Indonesia",
42545         "id",
42546         "62"
42547       ],
42548       [
42549         "Iran (‫ایران‬‎)",
42550         "ir",
42551         "98"
42552       ],
42553       [
42554         "Iraq (‫العراق‬‎)",
42555         "iq",
42556         "964"
42557       ],
42558       [
42559         "Ireland",
42560         "ie",
42561         "353"
42562       ],
42563       [
42564         "Isle of Man",
42565         "im",
42566         "44",
42567         2
42568       ],
42569       [
42570         "Israel (‫ישראל‬‎)",
42571         "il",
42572         "972"
42573       ],
42574       [
42575         "Italy (Italia)",
42576         "it",
42577         "39",
42578         0
42579       ],
42580       [
42581         "Jamaica",
42582         "jm",
42583         "1876"
42584       ],
42585       [
42586         "Japan (日本)",
42587         "jp",
42588         "81"
42589       ],
42590       [
42591         "Jersey",
42592         "je",
42593         "44",
42594         3
42595       ],
42596       [
42597         "Jordan (‫الأردن‬‎)",
42598         "jo",
42599         "962"
42600       ],
42601       [
42602         "Kazakhstan (Казахстан)",
42603         "kz",
42604         "7",
42605         1
42606       ],
42607       [
42608         "Kenya",
42609         "ke",
42610         "254"
42611       ],
42612       [
42613         "Kiribati",
42614         "ki",
42615         "686"
42616       ],
42617       [
42618         "Kosovo",
42619         "xk",
42620         "383"
42621       ],
42622       [
42623         "Kuwait (‫الكويت‬‎)",
42624         "kw",
42625         "965"
42626       ],
42627       [
42628         "Kyrgyzstan (Кыргызстан)",
42629         "kg",
42630         "996"
42631       ],
42632       [
42633         "Laos (ລາວ)",
42634         "la",
42635         "856"
42636       ],
42637       [
42638         "Latvia (Latvija)",
42639         "lv",
42640         "371"
42641       ],
42642       [
42643         "Lebanon (‫لبنان‬‎)",
42644         "lb",
42645         "961"
42646       ],
42647       [
42648         "Lesotho",
42649         "ls",
42650         "266"
42651       ],
42652       [
42653         "Liberia",
42654         "lr",
42655         "231"
42656       ],
42657       [
42658         "Libya (‫ليبيا‬‎)",
42659         "ly",
42660         "218"
42661       ],
42662       [
42663         "Liechtenstein",
42664         "li",
42665         "423"
42666       ],
42667       [
42668         "Lithuania (Lietuva)",
42669         "lt",
42670         "370"
42671       ],
42672       [
42673         "Luxembourg",
42674         "lu",
42675         "352"
42676       ],
42677       [
42678         "Macau (澳門)",
42679         "mo",
42680         "853"
42681       ],
42682       [
42683         "Macedonia (FYROM) (Македонија)",
42684         "mk",
42685         "389"
42686       ],
42687       [
42688         "Madagascar (Madagasikara)",
42689         "mg",
42690         "261"
42691       ],
42692       [
42693         "Malawi",
42694         "mw",
42695         "265"
42696       ],
42697       [
42698         "Malaysia",
42699         "my",
42700         "60"
42701       ],
42702       [
42703         "Maldives",
42704         "mv",
42705         "960"
42706       ],
42707       [
42708         "Mali",
42709         "ml",
42710         "223"
42711       ],
42712       [
42713         "Malta",
42714         "mt",
42715         "356"
42716       ],
42717       [
42718         "Marshall Islands",
42719         "mh",
42720         "692"
42721       ],
42722       [
42723         "Martinique",
42724         "mq",
42725         "596"
42726       ],
42727       [
42728         "Mauritania (‫موريتانيا‬‎)",
42729         "mr",
42730         "222"
42731       ],
42732       [
42733         "Mauritius (Moris)",
42734         "mu",
42735         "230"
42736       ],
42737       [
42738         "Mayotte",
42739         "yt",
42740         "262",
42741         1
42742       ],
42743       [
42744         "Mexico (México)",
42745         "mx",
42746         "52"
42747       ],
42748       [
42749         "Micronesia",
42750         "fm",
42751         "691"
42752       ],
42753       [
42754         "Moldova (Republica Moldova)",
42755         "md",
42756         "373"
42757       ],
42758       [
42759         "Monaco",
42760         "mc",
42761         "377"
42762       ],
42763       [
42764         "Mongolia (Монгол)",
42765         "mn",
42766         "976"
42767       ],
42768       [
42769         "Montenegro (Crna Gora)",
42770         "me",
42771         "382"
42772       ],
42773       [
42774         "Montserrat",
42775         "ms",
42776         "1664"
42777       ],
42778       [
42779         "Morocco (‫المغرب‬‎)",
42780         "ma",
42781         "212",
42782         0
42783       ],
42784       [
42785         "Mozambique (Moçambique)",
42786         "mz",
42787         "258"
42788       ],
42789       [
42790         "Myanmar (Burma) (မြန်မာ)",
42791         "mm",
42792         "95"
42793       ],
42794       [
42795         "Namibia (Namibië)",
42796         "na",
42797         "264"
42798       ],
42799       [
42800         "Nauru",
42801         "nr",
42802         "674"
42803       ],
42804       [
42805         "Nepal (नेपाल)",
42806         "np",
42807         "977"
42808       ],
42809       [
42810         "Netherlands (Nederland)",
42811         "nl",
42812         "31"
42813       ],
42814       [
42815         "New Caledonia (Nouvelle-Calédonie)",
42816         "nc",
42817         "687"
42818       ],
42819       [
42820         "New Zealand",
42821         "nz",
42822         "64"
42823       ],
42824       [
42825         "Nicaragua",
42826         "ni",
42827         "505"
42828       ],
42829       [
42830         "Niger (Nijar)",
42831         "ne",
42832         "227"
42833       ],
42834       [
42835         "Nigeria",
42836         "ng",
42837         "234"
42838       ],
42839       [
42840         "Niue",
42841         "nu",
42842         "683"
42843       ],
42844       [
42845         "Norfolk Island",
42846         "nf",
42847         "672"
42848       ],
42849       [
42850         "North Korea (조선 민주주의 인민 공화국)",
42851         "kp",
42852         "850"
42853       ],
42854       [
42855         "Northern Mariana Islands",
42856         "mp",
42857         "1670"
42858       ],
42859       [
42860         "Norway (Norge)",
42861         "no",
42862         "47",
42863         0
42864       ],
42865       [
42866         "Oman (‫عُمان‬‎)",
42867         "om",
42868         "968"
42869       ],
42870       [
42871         "Pakistan (‫پاکستان‬‎)",
42872         "pk",
42873         "92"
42874       ],
42875       [
42876         "Palau",
42877         "pw",
42878         "680"
42879       ],
42880       [
42881         "Palestine (‫فلسطين‬‎)",
42882         "ps",
42883         "970"
42884       ],
42885       [
42886         "Panama (Panamá)",
42887         "pa",
42888         "507"
42889       ],
42890       [
42891         "Papua New Guinea",
42892         "pg",
42893         "675"
42894       ],
42895       [
42896         "Paraguay",
42897         "py",
42898         "595"
42899       ],
42900       [
42901         "Peru (Perú)",
42902         "pe",
42903         "51"
42904       ],
42905       [
42906         "Philippines",
42907         "ph",
42908         "63"
42909       ],
42910       [
42911         "Poland (Polska)",
42912         "pl",
42913         "48"
42914       ],
42915       [
42916         "Portugal",
42917         "pt",
42918         "351"
42919       ],
42920       [
42921         "Puerto Rico",
42922         "pr",
42923         "1",
42924         3,
42925         ["787", "939"]
42926       ],
42927       [
42928         "Qatar (‫قطر‬‎)",
42929         "qa",
42930         "974"
42931       ],
42932       [
42933         "Réunion (La Réunion)",
42934         "re",
42935         "262",
42936         0
42937       ],
42938       [
42939         "Romania (România)",
42940         "ro",
42941         "40"
42942       ],
42943       [
42944         "Russia (Россия)",
42945         "ru",
42946         "7",
42947         0
42948       ],
42949       [
42950         "Rwanda",
42951         "rw",
42952         "250"
42953       ],
42954       [
42955         "Saint Barthélemy",
42956         "bl",
42957         "590",
42958         1
42959       ],
42960       [
42961         "Saint Helena",
42962         "sh",
42963         "290"
42964       ],
42965       [
42966         "Saint Kitts and Nevis",
42967         "kn",
42968         "1869"
42969       ],
42970       [
42971         "Saint Lucia",
42972         "lc",
42973         "1758"
42974       ],
42975       [
42976         "Saint Martin (Saint-Martin (partie française))",
42977         "mf",
42978         "590",
42979         2
42980       ],
42981       [
42982         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42983         "pm",
42984         "508"
42985       ],
42986       [
42987         "Saint Vincent and the Grenadines",
42988         "vc",
42989         "1784"
42990       ],
42991       [
42992         "Samoa",
42993         "ws",
42994         "685"
42995       ],
42996       [
42997         "San Marino",
42998         "sm",
42999         "378"
43000       ],
43001       [
43002         "São Tomé and Príncipe (São Tomé e Príncipe)",
43003         "st",
43004         "239"
43005       ],
43006       [
43007         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43008         "sa",
43009         "966"
43010       ],
43011       [
43012         "Senegal (Sénégal)",
43013         "sn",
43014         "221"
43015       ],
43016       [
43017         "Serbia (Србија)",
43018         "rs",
43019         "381"
43020       ],
43021       [
43022         "Seychelles",
43023         "sc",
43024         "248"
43025       ],
43026       [
43027         "Sierra Leone",
43028         "sl",
43029         "232"
43030       ],
43031       [
43032         "Singapore",
43033         "sg",
43034         "65"
43035       ],
43036       [
43037         "Sint Maarten",
43038         "sx",
43039         "1721"
43040       ],
43041       [
43042         "Slovakia (Slovensko)",
43043         "sk",
43044         "421"
43045       ],
43046       [
43047         "Slovenia (Slovenija)",
43048         "si",
43049         "386"
43050       ],
43051       [
43052         "Solomon Islands",
43053         "sb",
43054         "677"
43055       ],
43056       [
43057         "Somalia (Soomaaliya)",
43058         "so",
43059         "252"
43060       ],
43061       [
43062         "South Africa",
43063         "za",
43064         "27"
43065       ],
43066       [
43067         "South Korea (대한민국)",
43068         "kr",
43069         "82"
43070       ],
43071       [
43072         "South Sudan (‫جنوب السودان‬‎)",
43073         "ss",
43074         "211"
43075       ],
43076       [
43077         "Spain (España)",
43078         "es",
43079         "34"
43080       ],
43081       [
43082         "Sri Lanka (ශ්‍රී ලංකාව)",
43083         "lk",
43084         "94"
43085       ],
43086       [
43087         "Sudan (‫السودان‬‎)",
43088         "sd",
43089         "249"
43090       ],
43091       [
43092         "Suriname",
43093         "sr",
43094         "597"
43095       ],
43096       [
43097         "Svalbard and Jan Mayen",
43098         "sj",
43099         "47",
43100         1
43101       ],
43102       [
43103         "Swaziland",
43104         "sz",
43105         "268"
43106       ],
43107       [
43108         "Sweden (Sverige)",
43109         "se",
43110         "46"
43111       ],
43112       [
43113         "Switzerland (Schweiz)",
43114         "ch",
43115         "41"
43116       ],
43117       [
43118         "Syria (‫سوريا‬‎)",
43119         "sy",
43120         "963"
43121       ],
43122       [
43123         "Taiwan (台灣)",
43124         "tw",
43125         "886"
43126       ],
43127       [
43128         "Tajikistan",
43129         "tj",
43130         "992"
43131       ],
43132       [
43133         "Tanzania",
43134         "tz",
43135         "255"
43136       ],
43137       [
43138         "Thailand (ไทย)",
43139         "th",
43140         "66"
43141       ],
43142       [
43143         "Timor-Leste",
43144         "tl",
43145         "670"
43146       ],
43147       [
43148         "Togo",
43149         "tg",
43150         "228"
43151       ],
43152       [
43153         "Tokelau",
43154         "tk",
43155         "690"
43156       ],
43157       [
43158         "Tonga",
43159         "to",
43160         "676"
43161       ],
43162       [
43163         "Trinidad and Tobago",
43164         "tt",
43165         "1868"
43166       ],
43167       [
43168         "Tunisia (‫تونس‬‎)",
43169         "tn",
43170         "216"
43171       ],
43172       [
43173         "Turkey (Türkiye)",
43174         "tr",
43175         "90"
43176       ],
43177       [
43178         "Turkmenistan",
43179         "tm",
43180         "993"
43181       ],
43182       [
43183         "Turks and Caicos Islands",
43184         "tc",
43185         "1649"
43186       ],
43187       [
43188         "Tuvalu",
43189         "tv",
43190         "688"
43191       ],
43192       [
43193         "U.S. Virgin Islands",
43194         "vi",
43195         "1340"
43196       ],
43197       [
43198         "Uganda",
43199         "ug",
43200         "256"
43201       ],
43202       [
43203         "Ukraine (Україна)",
43204         "ua",
43205         "380"
43206       ],
43207       [
43208         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43209         "ae",
43210         "971"
43211       ],
43212       [
43213         "United Kingdom",
43214         "gb",
43215         "44",
43216         0
43217       ],
43218       [
43219         "United States",
43220         "us",
43221         "1",
43222         0
43223       ],
43224       [
43225         "Uruguay",
43226         "uy",
43227         "598"
43228       ],
43229       [
43230         "Uzbekistan (Oʻzbekiston)",
43231         "uz",
43232         "998"
43233       ],
43234       [
43235         "Vanuatu",
43236         "vu",
43237         "678"
43238       ],
43239       [
43240         "Vatican City (Città del Vaticano)",
43241         "va",
43242         "39",
43243         1
43244       ],
43245       [
43246         "Venezuela",
43247         "ve",
43248         "58"
43249       ],
43250       [
43251         "Vietnam (Việt Nam)",
43252         "vn",
43253         "84"
43254       ],
43255       [
43256         "Wallis and Futuna (Wallis-et-Futuna)",
43257         "wf",
43258         "681"
43259       ],
43260       [
43261         "Western Sahara (‫الصحراء الغربية‬‎)",
43262         "eh",
43263         "212",
43264         1
43265       ],
43266       [
43267         "Yemen (‫اليمن‬‎)",
43268         "ye",
43269         "967"
43270       ],
43271       [
43272         "Zambia",
43273         "zm",
43274         "260"
43275       ],
43276       [
43277         "Zimbabwe",
43278         "zw",
43279         "263"
43280       ],
43281       [
43282         "Åland Islands",
43283         "ax",
43284         "358",
43285         1
43286       ]
43287   ];
43288   
43289   return d;
43290 }/**
43291 *    This script refer to:
43292 *    Title: International Telephone Input
43293 *    Author: Jack O'Connor
43294 *    Code version:  v12.1.12
43295 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43296 **/
43297
43298 /**
43299  * @class Roo.bootstrap.PhoneInput
43300  * @extends Roo.bootstrap.TriggerField
43301  * An input with International dial-code selection
43302  
43303  * @cfg {String} defaultDialCode default '+852'
43304  * @cfg {Array} preferedCountries default []
43305   
43306  * @constructor
43307  * Create a new PhoneInput.
43308  * @param {Object} config Configuration options
43309  */
43310
43311 Roo.bootstrap.PhoneInput = function(config) {
43312     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43313 };
43314
43315 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43316         /**
43317         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43318         */
43319         listWidth: undefined,
43320         
43321         selectedClass: 'active',
43322         
43323         invalidClass : "has-warning",
43324         
43325         validClass: 'has-success',
43326         
43327         allowed: '0123456789',
43328         
43329         max_length: 15,
43330         
43331         /**
43332          * @cfg {String} defaultDialCode The default dial code when initializing the input
43333          */
43334         defaultDialCode: '+852',
43335         
43336         /**
43337          * @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
43338          */
43339         preferedCountries: false,
43340         
43341         getAutoCreate : function()
43342         {
43343             var data = Roo.bootstrap.PhoneInputData();
43344             var align = this.labelAlign || this.parentLabelAlign();
43345             var id = Roo.id();
43346             
43347             this.allCountries = [];
43348             this.dialCodeMapping = [];
43349             
43350             for (var i = 0; i < data.length; i++) {
43351               var c = data[i];
43352               this.allCountries[i] = {
43353                 name: c[0],
43354                 iso2: c[1],
43355                 dialCode: c[2],
43356                 priority: c[3] || 0,
43357                 areaCodes: c[4] || null
43358               };
43359               this.dialCodeMapping[c[2]] = {
43360                   name: c[0],
43361                   iso2: c[1],
43362                   priority: c[3] || 0,
43363                   areaCodes: c[4] || null
43364               };
43365             }
43366             
43367             var cfg = {
43368                 cls: 'form-group',
43369                 cn: []
43370             };
43371             
43372             var input =  {
43373                 tag: 'input',
43374                 id : id,
43375                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43376                 maxlength: this.max_length,
43377                 cls : 'form-control tel-input',
43378                 autocomplete: 'new-password'
43379             };
43380             
43381             var hiddenInput = {
43382                 tag: 'input',
43383                 type: 'hidden',
43384                 cls: 'hidden-tel-input'
43385             };
43386             
43387             if (this.name) {
43388                 hiddenInput.name = this.name;
43389             }
43390             
43391             if (this.disabled) {
43392                 input.disabled = true;
43393             }
43394             
43395             var flag_container = {
43396                 tag: 'div',
43397                 cls: 'flag-box',
43398                 cn: [
43399                     {
43400                         tag: 'div',
43401                         cls: 'flag'
43402                     },
43403                     {
43404                         tag: 'div',
43405                         cls: 'caret'
43406                     }
43407                 ]
43408             };
43409             
43410             var box = {
43411                 tag: 'div',
43412                 cls: this.hasFeedback ? 'has-feedback' : '',
43413                 cn: [
43414                     hiddenInput,
43415                     input,
43416                     {
43417                         tag: 'input',
43418                         cls: 'dial-code-holder',
43419                         disabled: true
43420                     }
43421                 ]
43422             };
43423             
43424             var container = {
43425                 cls: 'roo-select2-container input-group',
43426                 cn: [
43427                     flag_container,
43428                     box
43429                 ]
43430             };
43431             
43432             if (this.fieldLabel.length) {
43433                 var indicator = {
43434                     tag: 'i',
43435                     tooltip: 'This field is required'
43436                 };
43437                 
43438                 var label = {
43439                     tag: 'label',
43440                     'for':  id,
43441                     cls: 'control-label',
43442                     cn: []
43443                 };
43444                 
43445                 var label_text = {
43446                     tag: 'span',
43447                     html: this.fieldLabel
43448                 };
43449                 
43450                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43451                 label.cn = [
43452                     indicator,
43453                     label_text
43454                 ];
43455                 
43456                 if(this.indicatorpos == 'right') {
43457                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43458                     label.cn = [
43459                         label_text,
43460                         indicator
43461                     ];
43462                 }
43463                 
43464                 if(align == 'left') {
43465                     container = {
43466                         tag: 'div',
43467                         cn: [
43468                             container
43469                         ]
43470                     };
43471                     
43472                     if(this.labelWidth > 12){
43473                         label.style = "width: " + this.labelWidth + 'px';
43474                     }
43475                     if(this.labelWidth < 13 && this.labelmd == 0){
43476                         this.labelmd = this.labelWidth;
43477                     }
43478                     if(this.labellg > 0){
43479                         label.cls += ' col-lg-' + this.labellg;
43480                         input.cls += ' col-lg-' + (12 - this.labellg);
43481                     }
43482                     if(this.labelmd > 0){
43483                         label.cls += ' col-md-' + this.labelmd;
43484                         container.cls += ' col-md-' + (12 - this.labelmd);
43485                     }
43486                     if(this.labelsm > 0){
43487                         label.cls += ' col-sm-' + this.labelsm;
43488                         container.cls += ' col-sm-' + (12 - this.labelsm);
43489                     }
43490                     if(this.labelxs > 0){
43491                         label.cls += ' col-xs-' + this.labelxs;
43492                         container.cls += ' col-xs-' + (12 - this.labelxs);
43493                     }
43494                 }
43495             }
43496             
43497             cfg.cn = [
43498                 label,
43499                 container
43500             ];
43501             
43502             var settings = this;
43503             
43504             ['xs','sm','md','lg'].map(function(size){
43505                 if (settings[size]) {
43506                     cfg.cls += ' col-' + size + '-' + settings[size];
43507                 }
43508             });
43509             
43510             this.store = new Roo.data.Store({
43511                 proxy : new Roo.data.MemoryProxy({}),
43512                 reader : new Roo.data.JsonReader({
43513                     fields : [
43514                         {
43515                             'name' : 'name',
43516                             'type' : 'string'
43517                         },
43518                         {
43519                             'name' : 'iso2',
43520                             'type' : 'string'
43521                         },
43522                         {
43523                             'name' : 'dialCode',
43524                             'type' : 'string'
43525                         },
43526                         {
43527                             'name' : 'priority',
43528                             'type' : 'string'
43529                         },
43530                         {
43531                             'name' : 'areaCodes',
43532                             'type' : 'string'
43533                         }
43534                     ]
43535                 })
43536             });
43537             
43538             if(!this.preferedCountries) {
43539                 this.preferedCountries = [
43540                     'hk',
43541                     'gb',
43542                     'us'
43543                 ];
43544             }
43545             
43546             var p = this.preferedCountries.reverse();
43547             
43548             if(p) {
43549                 for (var i = 0; i < p.length; i++) {
43550                     for (var j = 0; j < this.allCountries.length; j++) {
43551                         if(this.allCountries[j].iso2 == p[i]) {
43552                             var t = this.allCountries[j];
43553                             this.allCountries.splice(j,1);
43554                             this.allCountries.unshift(t);
43555                         }
43556                     } 
43557                 }
43558             }
43559             
43560             this.store.proxy.data = {
43561                 success: true,
43562                 data: this.allCountries
43563             };
43564             
43565             return cfg;
43566         },
43567         
43568         initEvents : function()
43569         {
43570             this.createList();
43571             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43572             
43573             this.indicator = this.indicatorEl();
43574             this.flag = this.flagEl();
43575             this.dialCodeHolder = this.dialCodeHolderEl();
43576             
43577             this.trigger = this.el.select('div.flag-box',true).first();
43578             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43579             
43580             var _this = this;
43581             
43582             (function(){
43583                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43584                 _this.list.setWidth(lw);
43585             }).defer(100);
43586             
43587             this.list.on('mouseover', this.onViewOver, this);
43588             this.list.on('mousemove', this.onViewMove, this);
43589             this.inputEl().on("keyup", this.onKeyUp, this);
43590             this.inputEl().on("keypress", this.onKeyPress, this);
43591             
43592             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43593
43594             this.view = new Roo.View(this.list, this.tpl, {
43595                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43596             });
43597             
43598             this.view.on('click', this.onViewClick, this);
43599             this.setValue(this.defaultDialCode);
43600         },
43601         
43602         onTriggerClick : function(e)
43603         {
43604             Roo.log('trigger click');
43605             if(this.disabled){
43606                 return;
43607             }
43608             
43609             if(this.isExpanded()){
43610                 this.collapse();
43611                 this.hasFocus = false;
43612             }else {
43613                 this.store.load({});
43614                 this.hasFocus = true;
43615                 this.expand();
43616             }
43617         },
43618         
43619         isExpanded : function()
43620         {
43621             return this.list.isVisible();
43622         },
43623         
43624         collapse : function()
43625         {
43626             if(!this.isExpanded()){
43627                 return;
43628             }
43629             this.list.hide();
43630             Roo.get(document).un('mousedown', this.collapseIf, this);
43631             Roo.get(document).un('mousewheel', this.collapseIf, this);
43632             this.fireEvent('collapse', this);
43633             this.validate();
43634         },
43635         
43636         expand : function()
43637         {
43638             Roo.log('expand');
43639
43640             if(this.isExpanded() || !this.hasFocus){
43641                 return;
43642             }
43643             
43644             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43645             this.list.setWidth(lw);
43646             
43647             this.list.show();
43648             this.restrictHeight();
43649             
43650             Roo.get(document).on('mousedown', this.collapseIf, this);
43651             Roo.get(document).on('mousewheel', this.collapseIf, this);
43652             
43653             this.fireEvent('expand', this);
43654         },
43655         
43656         restrictHeight : function()
43657         {
43658             this.list.alignTo(this.inputEl(), this.listAlign);
43659             this.list.alignTo(this.inputEl(), this.listAlign);
43660         },
43661         
43662         onViewOver : function(e, t)
43663         {
43664             if(this.inKeyMode){
43665                 return;
43666             }
43667             var item = this.view.findItemFromChild(t);
43668             
43669             if(item){
43670                 var index = this.view.indexOf(item);
43671                 this.select(index, false);
43672             }
43673         },
43674
43675         // private
43676         onViewClick : function(view, doFocus, el, e)
43677         {
43678             var index = this.view.getSelectedIndexes()[0];
43679             
43680             var r = this.store.getAt(index);
43681             
43682             if(r){
43683                 this.onSelect(r, index);
43684             }
43685             if(doFocus !== false && !this.blockFocus){
43686                 this.inputEl().focus();
43687             }
43688         },
43689         
43690         onViewMove : function(e, t)
43691         {
43692             this.inKeyMode = false;
43693         },
43694         
43695         select : function(index, scrollIntoView)
43696         {
43697             this.selectedIndex = index;
43698             this.view.select(index);
43699             if(scrollIntoView !== false){
43700                 var el = this.view.getNode(index);
43701                 if(el){
43702                     this.list.scrollChildIntoView(el, false);
43703                 }
43704             }
43705         },
43706         
43707         createList : function()
43708         {
43709             this.list = Roo.get(document.body).createChild({
43710                 tag: 'ul',
43711                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43712                 style: 'display:none'
43713             });
43714             
43715             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43716         },
43717         
43718         collapseIf : function(e)
43719         {
43720             var in_combo  = e.within(this.el);
43721             var in_list =  e.within(this.list);
43722             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43723             
43724             if (in_combo || in_list || is_list) {
43725                 return;
43726             }
43727             this.collapse();
43728         },
43729         
43730         onSelect : function(record, index)
43731         {
43732             if(this.fireEvent('beforeselect', this, record, index) !== false){
43733                 
43734                 this.setFlagClass(record.data.iso2);
43735                 this.setDialCode(record.data.dialCode);
43736                 this.hasFocus = false;
43737                 this.collapse();
43738                 this.fireEvent('select', this, record, index);
43739             }
43740         },
43741         
43742         flagEl : function()
43743         {
43744             var flag = this.el.select('div.flag',true).first();
43745             if(!flag){
43746                 return false;
43747             }
43748             return flag;
43749         },
43750         
43751         dialCodeHolderEl : function()
43752         {
43753             var d = this.el.select('input.dial-code-holder',true).first();
43754             if(!d){
43755                 return false;
43756             }
43757             return d;
43758         },
43759         
43760         setDialCode : function(v)
43761         {
43762             this.dialCodeHolder.dom.value = '+'+v;
43763         },
43764         
43765         setFlagClass : function(n)
43766         {
43767             this.flag.dom.className = 'flag '+n;
43768         },
43769         
43770         getValue : function()
43771         {
43772             var v = this.inputEl().getValue();
43773             if(this.dialCodeHolder) {
43774                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43775             }
43776             return v;
43777         },
43778         
43779         setValue : function(v)
43780         {
43781             var d = this.getDialCode(v);
43782             
43783             //invalid dial code
43784             if(v.length == 0 || !d || d.length == 0) {
43785                 if(this.rendered){
43786                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43787                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43788                 }
43789                 return;
43790             }
43791             
43792             //valid dial code
43793             this.setFlagClass(this.dialCodeMapping[d].iso2);
43794             this.setDialCode(d);
43795             this.inputEl().dom.value = v.replace('+'+d,'');
43796             this.hiddenEl().dom.value = this.getValue();
43797             
43798             this.validate();
43799         },
43800         
43801         getDialCode : function(v)
43802         {
43803             v = v ||  '';
43804             
43805             if (v.length == 0) {
43806                 return this.dialCodeHolder.dom.value;
43807             }
43808             
43809             var dialCode = "";
43810             if (v.charAt(0) != "+") {
43811                 return false;
43812             }
43813             var numericChars = "";
43814             for (var i = 1; i < v.length; i++) {
43815               var c = v.charAt(i);
43816               if (!isNaN(c)) {
43817                 numericChars += c;
43818                 if (this.dialCodeMapping[numericChars]) {
43819                   dialCode = v.substr(1, i);
43820                 }
43821                 if (numericChars.length == 4) {
43822                   break;
43823                 }
43824               }
43825             }
43826             return dialCode;
43827         },
43828         
43829         reset : function()
43830         {
43831             this.setValue(this.defaultDialCode);
43832             this.validate();
43833         },
43834         
43835         hiddenEl : function()
43836         {
43837             return this.el.select('input.hidden-tel-input',true).first();
43838         },
43839         
43840         // after setting val
43841         onKeyUp : function(e){
43842             this.setValue(this.getValue());
43843         },
43844         
43845         onKeyPress : function(e){
43846             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43847                 e.stopEvent();
43848             }
43849         }
43850         
43851 });
43852 /**
43853  * @class Roo.bootstrap.MoneyField
43854  * @extends Roo.bootstrap.ComboBox
43855  * Bootstrap MoneyField class
43856  * 
43857  * @constructor
43858  * Create a new MoneyField.
43859  * @param {Object} config Configuration options
43860  */
43861
43862 Roo.bootstrap.MoneyField = function(config) {
43863     
43864     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43865     
43866 };
43867
43868 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43869     
43870     /**
43871      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43872      */
43873     allowDecimals : true,
43874     /**
43875      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43876      */
43877     decimalSeparator : ".",
43878     /**
43879      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43880      */
43881     decimalPrecision : 0,
43882     /**
43883      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43884      */
43885     allowNegative : true,
43886     /**
43887      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43888      */
43889     allowZero: true,
43890     /**
43891      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43892      */
43893     minValue : Number.NEGATIVE_INFINITY,
43894     /**
43895      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43896      */
43897     maxValue : Number.MAX_VALUE,
43898     /**
43899      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43900      */
43901     minText : "The minimum value for this field is {0}",
43902     /**
43903      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43904      */
43905     maxText : "The maximum value for this field is {0}",
43906     /**
43907      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43908      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43909      */
43910     nanText : "{0} is not a valid number",
43911     /**
43912      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43913      */
43914     castInt : true,
43915     /**
43916      * @cfg {String} defaults currency of the MoneyField
43917      * value should be in lkey
43918      */
43919     defaultCurrency : false,
43920     /**
43921      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43922      */
43923     thousandsDelimiter : false,
43924     /**
43925      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43926      */
43927     max_length: false,
43928     
43929     inputlg : 9,
43930     inputmd : 9,
43931     inputsm : 9,
43932     inputxs : 6,
43933      /**
43934      * @cfg {Roo.data.Store} store  Store to lookup currency??
43935      */
43936     store : false,
43937     
43938     getAutoCreate : function()
43939     {
43940         var align = this.labelAlign || this.parentLabelAlign();
43941         
43942         var id = Roo.id();
43943
43944         var cfg = {
43945             cls: 'form-group',
43946             cn: []
43947         };
43948
43949         var input =  {
43950             tag: 'input',
43951             id : id,
43952             cls : 'form-control roo-money-amount-input',
43953             autocomplete: 'new-password'
43954         };
43955         
43956         var hiddenInput = {
43957             tag: 'input',
43958             type: 'hidden',
43959             id: Roo.id(),
43960             cls: 'hidden-number-input'
43961         };
43962         
43963         if(this.max_length) {
43964             input.maxlength = this.max_length; 
43965         }
43966         
43967         if (this.name) {
43968             hiddenInput.name = this.name;
43969         }
43970
43971         if (this.disabled) {
43972             input.disabled = true;
43973         }
43974
43975         var clg = 12 - this.inputlg;
43976         var cmd = 12 - this.inputmd;
43977         var csm = 12 - this.inputsm;
43978         var cxs = 12 - this.inputxs;
43979         
43980         var container = {
43981             tag : 'div',
43982             cls : 'row roo-money-field',
43983             cn : [
43984                 {
43985                     tag : 'div',
43986                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43987                     cn : [
43988                         {
43989                             tag : 'div',
43990                             cls: 'roo-select2-container input-group',
43991                             cn: [
43992                                 {
43993                                     tag : 'input',
43994                                     cls : 'form-control roo-money-currency-input',
43995                                     autocomplete: 'new-password',
43996                                     readOnly : 1,
43997                                     name : this.currencyName
43998                                 },
43999                                 {
44000                                     tag :'span',
44001                                     cls : 'input-group-addon',
44002                                     cn : [
44003                                         {
44004                                             tag: 'span',
44005                                             cls: 'caret'
44006                                         }
44007                                     ]
44008                                 }
44009                             ]
44010                         }
44011                     ]
44012                 },
44013                 {
44014                     tag : 'div',
44015                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44016                     cn : [
44017                         {
44018                             tag: 'div',
44019                             cls: this.hasFeedback ? 'has-feedback' : '',
44020                             cn: [
44021                                 input
44022                             ]
44023                         }
44024                     ]
44025                 }
44026             ]
44027             
44028         };
44029         
44030         if (this.fieldLabel.length) {
44031             var indicator = {
44032                 tag: 'i',
44033                 tooltip: 'This field is required'
44034             };
44035
44036             var label = {
44037                 tag: 'label',
44038                 'for':  id,
44039                 cls: 'control-label',
44040                 cn: []
44041             };
44042
44043             var label_text = {
44044                 tag: 'span',
44045                 html: this.fieldLabel
44046             };
44047
44048             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44049             label.cn = [
44050                 indicator,
44051                 label_text
44052             ];
44053
44054             if(this.indicatorpos == 'right') {
44055                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44056                 label.cn = [
44057                     label_text,
44058                     indicator
44059                 ];
44060             }
44061
44062             if(align == 'left') {
44063                 container = {
44064                     tag: 'div',
44065                     cn: [
44066                         container
44067                     ]
44068                 };
44069
44070                 if(this.labelWidth > 12){
44071                     label.style = "width: " + this.labelWidth + 'px';
44072                 }
44073                 if(this.labelWidth < 13 && this.labelmd == 0){
44074                     this.labelmd = this.labelWidth;
44075                 }
44076                 if(this.labellg > 0){
44077                     label.cls += ' col-lg-' + this.labellg;
44078                     input.cls += ' col-lg-' + (12 - this.labellg);
44079                 }
44080                 if(this.labelmd > 0){
44081                     label.cls += ' col-md-' + this.labelmd;
44082                     container.cls += ' col-md-' + (12 - this.labelmd);
44083                 }
44084                 if(this.labelsm > 0){
44085                     label.cls += ' col-sm-' + this.labelsm;
44086                     container.cls += ' col-sm-' + (12 - this.labelsm);
44087                 }
44088                 if(this.labelxs > 0){
44089                     label.cls += ' col-xs-' + this.labelxs;
44090                     container.cls += ' col-xs-' + (12 - this.labelxs);
44091                 }
44092             }
44093         }
44094
44095         cfg.cn = [
44096             label,
44097             container,
44098             hiddenInput
44099         ];
44100         
44101         var settings = this;
44102
44103         ['xs','sm','md','lg'].map(function(size){
44104             if (settings[size]) {
44105                 cfg.cls += ' col-' + size + '-' + settings[size];
44106             }
44107         });
44108         
44109         return cfg;
44110     },
44111     
44112     initEvents : function()
44113     {
44114         this.indicator = this.indicatorEl();
44115         
44116         this.initCurrencyEvent();
44117         
44118         this.initNumberEvent();
44119     },
44120     
44121     initCurrencyEvent : function()
44122     {
44123         if (!this.store) {
44124             throw "can not find store for combo";
44125         }
44126         
44127         this.store = Roo.factory(this.store, Roo.data);
44128         this.store.parent = this;
44129         
44130         this.createList();
44131         
44132         this.triggerEl = this.el.select('.input-group-addon', true).first();
44133         
44134         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44135         
44136         var _this = this;
44137         
44138         (function(){
44139             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44140             _this.list.setWidth(lw);
44141         }).defer(100);
44142         
44143         this.list.on('mouseover', this.onViewOver, this);
44144         this.list.on('mousemove', this.onViewMove, this);
44145         this.list.on('scroll', this.onViewScroll, this);
44146         
44147         if(!this.tpl){
44148             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44149         }
44150         
44151         this.view = new Roo.View(this.list, this.tpl, {
44152             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44153         });
44154         
44155         this.view.on('click', this.onViewClick, this);
44156         
44157         this.store.on('beforeload', this.onBeforeLoad, this);
44158         this.store.on('load', this.onLoad, this);
44159         this.store.on('loadexception', this.onLoadException, this);
44160         
44161         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44162             "up" : function(e){
44163                 this.inKeyMode = true;
44164                 this.selectPrev();
44165             },
44166
44167             "down" : function(e){
44168                 if(!this.isExpanded()){
44169                     this.onTriggerClick();
44170                 }else{
44171                     this.inKeyMode = true;
44172                     this.selectNext();
44173                 }
44174             },
44175
44176             "enter" : function(e){
44177                 this.collapse();
44178                 
44179                 if(this.fireEvent("specialkey", this, e)){
44180                     this.onViewClick(false);
44181                 }
44182                 
44183                 return true;
44184             },
44185
44186             "esc" : function(e){
44187                 this.collapse();
44188             },
44189
44190             "tab" : function(e){
44191                 this.collapse();
44192                 
44193                 if(this.fireEvent("specialkey", this, e)){
44194                     this.onViewClick(false);
44195                 }
44196                 
44197                 return true;
44198             },
44199
44200             scope : this,
44201
44202             doRelay : function(foo, bar, hname){
44203                 if(hname == 'down' || this.scope.isExpanded()){
44204                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44205                 }
44206                 return true;
44207             },
44208
44209             forceKeyDown: true
44210         });
44211         
44212         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44213         
44214     },
44215     
44216     initNumberEvent : function(e)
44217     {
44218         this.inputEl().on("keydown" , this.fireKey,  this);
44219         this.inputEl().on("focus", this.onFocus,  this);
44220         this.inputEl().on("blur", this.onBlur,  this);
44221         
44222         this.inputEl().relayEvent('keyup', this);
44223         
44224         if(this.indicator){
44225             this.indicator.addClass('invisible');
44226         }
44227  
44228         this.originalValue = this.getValue();
44229         
44230         if(this.validationEvent == 'keyup'){
44231             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44232             this.inputEl().on('keyup', this.filterValidation, this);
44233         }
44234         else if(this.validationEvent !== false){
44235             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44236         }
44237         
44238         if(this.selectOnFocus){
44239             this.on("focus", this.preFocus, this);
44240             
44241         }
44242         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44243             this.inputEl().on("keypress", this.filterKeys, this);
44244         } else {
44245             this.inputEl().relayEvent('keypress', this);
44246         }
44247         
44248         var allowed = "0123456789";
44249         
44250         if(this.allowDecimals){
44251             allowed += this.decimalSeparator;
44252         }
44253         
44254         if(this.allowNegative){
44255             allowed += "-";
44256         }
44257         
44258         if(this.thousandsDelimiter) {
44259             allowed += ",";
44260         }
44261         
44262         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44263         
44264         var keyPress = function(e){
44265             
44266             var k = e.getKey();
44267             
44268             var c = e.getCharCode();
44269             
44270             if(
44271                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44272                     allowed.indexOf(String.fromCharCode(c)) === -1
44273             ){
44274                 e.stopEvent();
44275                 return;
44276             }
44277             
44278             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44279                 return;
44280             }
44281             
44282             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44283                 e.stopEvent();
44284             }
44285         };
44286         
44287         this.inputEl().on("keypress", keyPress, this);
44288         
44289     },
44290     
44291     onTriggerClick : function(e)
44292     {   
44293         if(this.disabled){
44294             return;
44295         }
44296         
44297         this.page = 0;
44298         this.loadNext = false;
44299         
44300         if(this.isExpanded()){
44301             this.collapse();
44302             return;
44303         }
44304         
44305         this.hasFocus = true;
44306         
44307         if(this.triggerAction == 'all') {
44308             this.doQuery(this.allQuery, true);
44309             return;
44310         }
44311         
44312         this.doQuery(this.getRawValue());
44313     },
44314     
44315     getCurrency : function()
44316     {   
44317         var v = this.currencyEl().getValue();
44318         
44319         return v;
44320     },
44321     
44322     restrictHeight : function()
44323     {
44324         this.list.alignTo(this.currencyEl(), this.listAlign);
44325         this.list.alignTo(this.currencyEl(), this.listAlign);
44326     },
44327     
44328     onViewClick : function(view, doFocus, el, e)
44329     {
44330         var index = this.view.getSelectedIndexes()[0];
44331         
44332         var r = this.store.getAt(index);
44333         
44334         if(r){
44335             this.onSelect(r, index);
44336         }
44337     },
44338     
44339     onSelect : function(record, index){
44340         
44341         if(this.fireEvent('beforeselect', this, record, index) !== false){
44342         
44343             this.setFromCurrencyData(index > -1 ? record.data : false);
44344             
44345             this.collapse();
44346             
44347             this.fireEvent('select', this, record, index);
44348         }
44349     },
44350     
44351     setFromCurrencyData : function(o)
44352     {
44353         var currency = '';
44354         
44355         this.lastCurrency = o;
44356         
44357         if (this.currencyField) {
44358             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44359         } else {
44360             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44361         }
44362         
44363         this.lastSelectionText = currency;
44364         
44365         //setting default currency
44366         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44367             this.setCurrency(this.defaultCurrency);
44368             return;
44369         }
44370         
44371         this.setCurrency(currency);
44372     },
44373     
44374     setFromData : function(o)
44375     {
44376         var c = {};
44377         
44378         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44379         
44380         this.setFromCurrencyData(c);
44381         
44382         var value = '';
44383         
44384         if (this.name) {
44385             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44386         } else {
44387             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44388         }
44389         
44390         this.setValue(value);
44391         
44392     },
44393     
44394     setCurrency : function(v)
44395     {   
44396         this.currencyValue = v;
44397         
44398         if(this.rendered){
44399             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44400             this.validate();
44401         }
44402     },
44403     
44404     setValue : function(v)
44405     {
44406         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44407         
44408         this.value = v;
44409         
44410         if(this.rendered){
44411             
44412             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44413             
44414             this.inputEl().dom.value = (v == '') ? '' :
44415                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44416             
44417             if(!this.allowZero && v === '0') {
44418                 this.hiddenEl().dom.value = '';
44419                 this.inputEl().dom.value = '';
44420             }
44421             
44422             this.validate();
44423         }
44424     },
44425     
44426     getRawValue : function()
44427     {
44428         var v = this.inputEl().getValue();
44429         
44430         return v;
44431     },
44432     
44433     getValue : function()
44434     {
44435         return this.fixPrecision(this.parseValue(this.getRawValue()));
44436     },
44437     
44438     parseValue : function(value)
44439     {
44440         if(this.thousandsDelimiter) {
44441             value += "";
44442             r = new RegExp(",", "g");
44443             value = value.replace(r, "");
44444         }
44445         
44446         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44447         return isNaN(value) ? '' : value;
44448         
44449     },
44450     
44451     fixPrecision : function(value)
44452     {
44453         if(this.thousandsDelimiter) {
44454             value += "";
44455             r = new RegExp(",", "g");
44456             value = value.replace(r, "");
44457         }
44458         
44459         var nan = isNaN(value);
44460         
44461         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44462             return nan ? '' : value;
44463         }
44464         return parseFloat(value).toFixed(this.decimalPrecision);
44465     },
44466     
44467     decimalPrecisionFcn : function(v)
44468     {
44469         return Math.floor(v);
44470     },
44471     
44472     validateValue : function(value)
44473     {
44474         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44475             return false;
44476         }
44477         
44478         var num = this.parseValue(value);
44479         
44480         if(isNaN(num)){
44481             this.markInvalid(String.format(this.nanText, value));
44482             return false;
44483         }
44484         
44485         if(num < this.minValue){
44486             this.markInvalid(String.format(this.minText, this.minValue));
44487             return false;
44488         }
44489         
44490         if(num > this.maxValue){
44491             this.markInvalid(String.format(this.maxText, this.maxValue));
44492             return false;
44493         }
44494         
44495         return true;
44496     },
44497     
44498     validate : function()
44499     {
44500         if(this.disabled || this.allowBlank){
44501             this.markValid();
44502             return true;
44503         }
44504         
44505         var currency = this.getCurrency();
44506         
44507         if(this.validateValue(this.getRawValue()) && currency.length){
44508             this.markValid();
44509             return true;
44510         }
44511         
44512         this.markInvalid();
44513         return false;
44514     },
44515     
44516     getName: function()
44517     {
44518         return this.name;
44519     },
44520     
44521     beforeBlur : function()
44522     {
44523         if(!this.castInt){
44524             return;
44525         }
44526         
44527         var v = this.parseValue(this.getRawValue());
44528         
44529         if(v || v == 0){
44530             this.setValue(v);
44531         }
44532     },
44533     
44534     onBlur : function()
44535     {
44536         this.beforeBlur();
44537         
44538         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44539             //this.el.removeClass(this.focusClass);
44540         }
44541         
44542         this.hasFocus = false;
44543         
44544         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44545             this.validate();
44546         }
44547         
44548         var v = this.getValue();
44549         
44550         if(String(v) !== String(this.startValue)){
44551             this.fireEvent('change', this, v, this.startValue);
44552         }
44553         
44554         this.fireEvent("blur", this);
44555     },
44556     
44557     inputEl : function()
44558     {
44559         return this.el.select('.roo-money-amount-input', true).first();
44560     },
44561     
44562     currencyEl : function()
44563     {
44564         return this.el.select('.roo-money-currency-input', true).first();
44565     },
44566     
44567     hiddenEl : function()
44568     {
44569         return this.el.select('input.hidden-number-input',true).first();
44570     }
44571     
44572 });/**
44573  * @class Roo.bootstrap.BezierSignature
44574  * @extends Roo.bootstrap.Component
44575  * Bootstrap BezierSignature class
44576  * This script refer to:
44577  *    Title: Signature Pad
44578  *    Author: szimek
44579  *    Availability: https://github.com/szimek/signature_pad
44580  *
44581  * @constructor
44582  * Create a new BezierSignature
44583  * @param {Object} config The config object
44584  */
44585
44586 Roo.bootstrap.BezierSignature = function(config){
44587     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44588     this.addEvents({
44589         "resize" : true
44590     });
44591 };
44592
44593 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44594 {
44595      
44596     curve_data: [],
44597     
44598     is_empty: true,
44599     
44600     mouse_btn_down: true,
44601     
44602     /**
44603      * @cfg {int} canvas height
44604      */
44605     canvas_height: '200px',
44606     
44607     /**
44608      * @cfg {float|function} Radius of a single dot.
44609      */ 
44610     dot_size: false,
44611     
44612     /**
44613      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44614      */
44615     min_width: 0.5,
44616     
44617     /**
44618      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44619      */
44620     max_width: 2.5,
44621     
44622     /**
44623      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44624      */
44625     throttle: 16,
44626     
44627     /**
44628      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44629      */
44630     min_distance: 5,
44631     
44632     /**
44633      * @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.
44634      */
44635     bg_color: 'rgba(0, 0, 0, 0)',
44636     
44637     /**
44638      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44639      */
44640     dot_color: 'black',
44641     
44642     /**
44643      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44644      */ 
44645     velocity_filter_weight: 0.7,
44646     
44647     /**
44648      * @cfg {function} Callback when stroke begin. 
44649      */
44650     onBegin: false,
44651     
44652     /**
44653      * @cfg {function} Callback when stroke end.
44654      */
44655     onEnd: false,
44656     
44657     getAutoCreate : function()
44658     {
44659         var cls = 'roo-signature column';
44660         
44661         if(this.cls){
44662             cls += ' ' + this.cls;
44663         }
44664         
44665         var col_sizes = [
44666             'lg',
44667             'md',
44668             'sm',
44669             'xs'
44670         ];
44671         
44672         for(var i = 0; i < col_sizes.length; i++) {
44673             if(this[col_sizes[i]]) {
44674                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44675             }
44676         }
44677         
44678         var cfg = {
44679             tag: 'div',
44680             cls: cls,
44681             cn: [
44682                 {
44683                     tag: 'div',
44684                     cls: 'roo-signature-body',
44685                     cn: [
44686                         {
44687                             tag: 'canvas',
44688                             cls: 'roo-signature-body-canvas',
44689                             height: this.canvas_height,
44690                             width: this.canvas_width
44691                         }
44692                     ]
44693                 },
44694                 {
44695                     tag: 'input',
44696                     type: 'file',
44697                     style: 'display: none'
44698                 }
44699             ]
44700         };
44701         
44702         return cfg;
44703     },
44704     
44705     initEvents: function() 
44706     {
44707         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44708         
44709         var canvas = this.canvasEl();
44710         
44711         // mouse && touch event swapping...
44712         canvas.dom.style.touchAction = 'none';
44713         canvas.dom.style.msTouchAction = 'none';
44714         
44715         this.mouse_btn_down = false;
44716         canvas.on('mousedown', this._handleMouseDown, this);
44717         canvas.on('mousemove', this._handleMouseMove, this);
44718         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44719         
44720         if (window.PointerEvent) {
44721             canvas.on('pointerdown', this._handleMouseDown, this);
44722             canvas.on('pointermove', this._handleMouseMove, this);
44723             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44724         }
44725         
44726         if ('ontouchstart' in window) {
44727             canvas.on('touchstart', this._handleTouchStart, this);
44728             canvas.on('touchmove', this._handleTouchMove, this);
44729             canvas.on('touchend', this._handleTouchEnd, this);
44730         }
44731         
44732         Roo.EventManager.onWindowResize(this.resize, this, true);
44733         
44734         // file input event
44735         this.fileEl().on('change', this.uploadImage, this);
44736         
44737         this.clear();
44738         
44739         this.resize();
44740     },
44741     
44742     resize: function(){
44743         
44744         var canvas = this.canvasEl().dom;
44745         var ctx = this.canvasElCtx();
44746         var img_data = false;
44747         
44748         if(canvas.width > 0) {
44749             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44750         }
44751         // setting canvas width will clean img data
44752         canvas.width = 0;
44753         
44754         var style = window.getComputedStyle ? 
44755             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44756             
44757         var padding_left = parseInt(style.paddingLeft) || 0;
44758         var padding_right = parseInt(style.paddingRight) || 0;
44759         
44760         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44761         
44762         if(img_data) {
44763             ctx.putImageData(img_data, 0, 0);
44764         }
44765     },
44766     
44767     _handleMouseDown: function(e)
44768     {
44769         if (e.browserEvent.which === 1) {
44770             this.mouse_btn_down = true;
44771             this.strokeBegin(e);
44772         }
44773     },
44774     
44775     _handleMouseMove: function (e)
44776     {
44777         if (this.mouse_btn_down) {
44778             this.strokeMoveUpdate(e);
44779         }
44780     },
44781     
44782     _handleMouseUp: function (e)
44783     {
44784         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44785             this.mouse_btn_down = false;
44786             this.strokeEnd(e);
44787         }
44788     },
44789     
44790     _handleTouchStart: function (e) {
44791         
44792         e.preventDefault();
44793         if (e.browserEvent.targetTouches.length === 1) {
44794             // var touch = e.browserEvent.changedTouches[0];
44795             // this.strokeBegin(touch);
44796             
44797              this.strokeBegin(e); // assume e catching the correct xy...
44798         }
44799     },
44800     
44801     _handleTouchMove: function (e) {
44802         e.preventDefault();
44803         // var touch = event.targetTouches[0];
44804         // _this._strokeMoveUpdate(touch);
44805         this.strokeMoveUpdate(e);
44806     },
44807     
44808     _handleTouchEnd: function (e) {
44809         var wasCanvasTouched = e.target === this.canvasEl().dom;
44810         if (wasCanvasTouched) {
44811             e.preventDefault();
44812             // var touch = event.changedTouches[0];
44813             // _this._strokeEnd(touch);
44814             this.strokeEnd(e);
44815         }
44816     },
44817     
44818     reset: function () {
44819         this._lastPoints = [];
44820         this._lastVelocity = 0;
44821         this._lastWidth = (this.min_width + this.max_width) / 2;
44822         this.canvasElCtx().fillStyle = this.dot_color;
44823     },
44824     
44825     strokeMoveUpdate: function(e)
44826     {
44827         this.strokeUpdate(e);
44828         
44829         if (this.throttle) {
44830             this.throttleStroke(this.strokeUpdate, this.throttle);
44831         }
44832         else {
44833             this.strokeUpdate(e);
44834         }
44835     },
44836     
44837     strokeBegin: function(e)
44838     {
44839         var newPointGroup = {
44840             color: this.dot_color,
44841             points: []
44842         };
44843         
44844         if (typeof this.onBegin === 'function') {
44845             this.onBegin(e);
44846         }
44847         
44848         this.curve_data.push(newPointGroup);
44849         this.reset();
44850         this.strokeUpdate(e);
44851     },
44852     
44853     strokeUpdate: function(e)
44854     {
44855         var rect = this.canvasEl().dom.getBoundingClientRect();
44856         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44857         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44858         var lastPoints = lastPointGroup.points;
44859         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44860         var isLastPointTooClose = lastPoint
44861             ? point.distanceTo(lastPoint) <= this.min_distance
44862             : false;
44863         var color = lastPointGroup.color;
44864         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44865             var curve = this.addPoint(point);
44866             if (!lastPoint) {
44867                 this.drawDot({color: color, point: point});
44868             }
44869             else if (curve) {
44870                 this.drawCurve({color: color, curve: curve});
44871             }
44872             lastPoints.push({
44873                 time: point.time,
44874                 x: point.x,
44875                 y: point.y
44876             });
44877         }
44878     },
44879     
44880     strokeEnd: function(e)
44881     {
44882         this.strokeUpdate(e);
44883         if (typeof this.onEnd === 'function') {
44884             this.onEnd(e);
44885         }
44886     },
44887     
44888     addPoint:  function (point) {
44889         var _lastPoints = this._lastPoints;
44890         _lastPoints.push(point);
44891         if (_lastPoints.length > 2) {
44892             if (_lastPoints.length === 3) {
44893                 _lastPoints.unshift(_lastPoints[0]);
44894             }
44895             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44896             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44897             _lastPoints.shift();
44898             return curve;
44899         }
44900         return null;
44901     },
44902     
44903     calculateCurveWidths: function (startPoint, endPoint) {
44904         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44905             (1 - this.velocity_filter_weight) * this._lastVelocity;
44906
44907         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44908         var widths = {
44909             end: newWidth,
44910             start: this._lastWidth
44911         };
44912         
44913         this._lastVelocity = velocity;
44914         this._lastWidth = newWidth;
44915         return widths;
44916     },
44917     
44918     drawDot: function (_a) {
44919         var color = _a.color, point = _a.point;
44920         var ctx = this.canvasElCtx();
44921         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44922         ctx.beginPath();
44923         this.drawCurveSegment(point.x, point.y, width);
44924         ctx.closePath();
44925         ctx.fillStyle = color;
44926         ctx.fill();
44927     },
44928     
44929     drawCurve: function (_a) {
44930         var color = _a.color, curve = _a.curve;
44931         var ctx = this.canvasElCtx();
44932         var widthDelta = curve.endWidth - curve.startWidth;
44933         var drawSteps = Math.floor(curve.length()) * 2;
44934         ctx.beginPath();
44935         ctx.fillStyle = color;
44936         for (var i = 0; i < drawSteps; i += 1) {
44937         var t = i / drawSteps;
44938         var tt = t * t;
44939         var ttt = tt * t;
44940         var u = 1 - t;
44941         var uu = u * u;
44942         var uuu = uu * u;
44943         var x = uuu * curve.startPoint.x;
44944         x += 3 * uu * t * curve.control1.x;
44945         x += 3 * u * tt * curve.control2.x;
44946         x += ttt * curve.endPoint.x;
44947         var y = uuu * curve.startPoint.y;
44948         y += 3 * uu * t * curve.control1.y;
44949         y += 3 * u * tt * curve.control2.y;
44950         y += ttt * curve.endPoint.y;
44951         var width = curve.startWidth + ttt * widthDelta;
44952         this.drawCurveSegment(x, y, width);
44953         }
44954         ctx.closePath();
44955         ctx.fill();
44956     },
44957     
44958     drawCurveSegment: function (x, y, width) {
44959         var ctx = this.canvasElCtx();
44960         ctx.moveTo(x, y);
44961         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44962         this.is_empty = false;
44963     },
44964     
44965     clear: function()
44966     {
44967         var ctx = this.canvasElCtx();
44968         var canvas = this.canvasEl().dom;
44969         ctx.fillStyle = this.bg_color;
44970         ctx.clearRect(0, 0, canvas.width, canvas.height);
44971         ctx.fillRect(0, 0, canvas.width, canvas.height);
44972         this.curve_data = [];
44973         this.reset();
44974         this.is_empty = true;
44975     },
44976     
44977     fileEl: function()
44978     {
44979         return  this.el.select('input',true).first();
44980     },
44981     
44982     canvasEl: function()
44983     {
44984         return this.el.select('canvas',true).first();
44985     },
44986     
44987     canvasElCtx: function()
44988     {
44989         return this.el.select('canvas',true).first().dom.getContext('2d');
44990     },
44991     
44992     getImage: function(type)
44993     {
44994         if(this.is_empty) {
44995             return false;
44996         }
44997         
44998         // encryption ?
44999         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45000     },
45001     
45002     drawFromImage: function(img_src)
45003     {
45004         var img = new Image();
45005         
45006         img.onload = function(){
45007             this.canvasElCtx().drawImage(img, 0, 0);
45008         }.bind(this);
45009         
45010         img.src = img_src;
45011         
45012         this.is_empty = false;
45013     },
45014     
45015     selectImage: function()
45016     {
45017         this.fileEl().dom.click();
45018     },
45019     
45020     uploadImage: function(e)
45021     {
45022         var reader = new FileReader();
45023         
45024         reader.onload = function(e){
45025             var img = new Image();
45026             img.onload = function(){
45027                 this.reset();
45028                 this.canvasElCtx().drawImage(img, 0, 0);
45029             }.bind(this);
45030             img.src = e.target.result;
45031         }.bind(this);
45032         
45033         reader.readAsDataURL(e.target.files[0]);
45034     },
45035     
45036     // Bezier Point Constructor
45037     Point: (function () {
45038         function Point(x, y, time) {
45039             this.x = x;
45040             this.y = y;
45041             this.time = time || Date.now();
45042         }
45043         Point.prototype.distanceTo = function (start) {
45044             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45045         };
45046         Point.prototype.equals = function (other) {
45047             return this.x === other.x && this.y === other.y && this.time === other.time;
45048         };
45049         Point.prototype.velocityFrom = function (start) {
45050             return this.time !== start.time
45051             ? this.distanceTo(start) / (this.time - start.time)
45052             : 0;
45053         };
45054         return Point;
45055     }()),
45056     
45057     
45058     // Bezier Constructor
45059     Bezier: (function () {
45060         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45061             this.startPoint = startPoint;
45062             this.control2 = control2;
45063             this.control1 = control1;
45064             this.endPoint = endPoint;
45065             this.startWidth = startWidth;
45066             this.endWidth = endWidth;
45067         }
45068         Bezier.fromPoints = function (points, widths, scope) {
45069             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45070             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45071             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45072         };
45073         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45074             var dx1 = s1.x - s2.x;
45075             var dy1 = s1.y - s2.y;
45076             var dx2 = s2.x - s3.x;
45077             var dy2 = s2.y - s3.y;
45078             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45079             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45080             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45081             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45082             var dxm = m1.x - m2.x;
45083             var dym = m1.y - m2.y;
45084             var k = l2 / (l1 + l2);
45085             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45086             var tx = s2.x - cm.x;
45087             var ty = s2.y - cm.y;
45088             return {
45089                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45090                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45091             };
45092         };
45093         Bezier.prototype.length = function () {
45094             var steps = 10;
45095             var length = 0;
45096             var px;
45097             var py;
45098             for (var i = 0; i <= steps; i += 1) {
45099                 var t = i / steps;
45100                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45101                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45102                 if (i > 0) {
45103                     var xdiff = cx - px;
45104                     var ydiff = cy - py;
45105                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45106                 }
45107                 px = cx;
45108                 py = cy;
45109             }
45110             return length;
45111         };
45112         Bezier.prototype.point = function (t, start, c1, c2, end) {
45113             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45114             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45115             + (3.0 * c2 * (1.0 - t) * t * t)
45116             + (end * t * t * t);
45117         };
45118         return Bezier;
45119     }()),
45120     
45121     throttleStroke: function(fn, wait) {
45122       if (wait === void 0) { wait = 250; }
45123       var previous = 0;
45124       var timeout = null;
45125       var result;
45126       var storedContext;
45127       var storedArgs;
45128       var later = function () {
45129           previous = Date.now();
45130           timeout = null;
45131           result = fn.apply(storedContext, storedArgs);
45132           if (!timeout) {
45133               storedContext = null;
45134               storedArgs = [];
45135           }
45136       };
45137       return function wrapper() {
45138           var args = [];
45139           for (var _i = 0; _i < arguments.length; _i++) {
45140               args[_i] = arguments[_i];
45141           }
45142           var now = Date.now();
45143           var remaining = wait - (now - previous);
45144           storedContext = this;
45145           storedArgs = args;
45146           if (remaining <= 0 || remaining > wait) {
45147               if (timeout) {
45148                   clearTimeout(timeout);
45149                   timeout = null;
45150               }
45151               previous = now;
45152               result = fn.apply(storedContext, storedArgs);
45153               if (!timeout) {
45154                   storedContext = null;
45155                   storedArgs = [];
45156               }
45157           }
45158           else if (!timeout) {
45159               timeout = window.setTimeout(later, remaining);
45160           }
45161           return result;
45162       };
45163   }
45164   
45165 });
45166
45167  
45168
45169