sync
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets, function(s) {
10                     if ( s.href  && s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })(); /*
16  * Based on:
17  * Ext JS Library 1.1.1
18  * Copyright(c) 2006-2007, Ext JS, LLC.
19  *
20  * Originally Released Under LGPL - original licence link has changed is not relivant.
21  *
22  * Fork - LGPL
23  * <script type="text/javascript">
24  */
25
26
27 /**
28  * @class Roo.Shadow
29  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
30  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
31  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
32  * @constructor
33  * Create a new Shadow
34  * @param {Object} config The config object
35  */
36 Roo.Shadow = function(config){
37     Roo.apply(this, config);
38     if(typeof this.mode != "string"){
39         this.mode = this.defaultMode;
40     }
41     var o = this.offset, a = {h: 0};
42     var rad = Math.floor(this.offset/2);
43     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
44         case "drop":
45             a.w = 0;
46             a.l = a.t = o;
47             a.t -= 1;
48             if(Roo.isIE){
49                 a.l -= this.offset + rad;
50                 a.t -= this.offset + rad;
51                 a.w -= rad;
52                 a.h -= rad;
53                 a.t += 1;
54             }
55         break;
56         case "sides":
57             a.w = (o*2);
58             a.l = -o;
59             a.t = o-1;
60             if(Roo.isIE){
61                 a.l -= (this.offset - rad);
62                 a.t -= this.offset + rad;
63                 a.l += 1;
64                 a.w -= (this.offset - rad)*2;
65                 a.w -= rad + 1;
66                 a.h -= 1;
67             }
68         break;
69         case "frame":
70             a.w = a.h = (o*2);
71             a.l = a.t = -o;
72             a.t += 1;
73             a.h -= 2;
74             if(Roo.isIE){
75                 a.l -= (this.offset - rad);
76                 a.t -= (this.offset - rad);
77                 a.l += 1;
78                 a.w -= (this.offset + rad + 1);
79                 a.h -= (this.offset + rad);
80                 a.h += 1;
81             }
82         break;
83     };
84
85     this.adjusts = a;
86 };
87
88 Roo.Shadow.prototype = {
89     /**
90      * @cfg {String} mode
91      * The shadow display mode.  Supports the following options:<br />
92      * sides: Shadow displays on both sides and bottom only<br />
93      * frame: Shadow displays equally on all four sides<br />
94      * drop: Traditional bottom-right drop shadow (default)
95      */
96     /**
97      * @cfg {String} offset
98      * The number of pixels to offset the shadow from the element (defaults to 4)
99      */
100     offset: 4,
101
102     // private
103     defaultMode: "drop",
104
105     /**
106      * Displays the shadow under the target element
107      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
108      */
109     show : function(target){
110         target = Roo.get(target);
111         if(!this.el){
112             this.el = Roo.Shadow.Pool.pull();
113             if(this.el.dom.nextSibling != target.dom){
114                 this.el.insertBefore(target);
115             }
116         }
117         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
118         if(Roo.isIE){
119             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
120         }
121         this.realign(
122             target.getLeft(true),
123             target.getTop(true),
124             target.getWidth(),
125             target.getHeight()
126         );
127         this.el.dom.style.display = "block";
128     },
129
130     /**
131      * Returns true if the shadow is visible, else false
132      */
133     isVisible : function(){
134         return this.el ? true : false;  
135     },
136
137     /**
138      * Direct alignment when values are already available. Show must be called at least once before
139      * calling this method to ensure it is initialized.
140      * @param {Number} left The target element left position
141      * @param {Number} top The target element top position
142      * @param {Number} width The target element width
143      * @param {Number} height The target element height
144      */
145     realign : function(l, t, w, h){
146         if(!this.el){
147             return;
148         }
149         var a = this.adjusts, d = this.el.dom, s = d.style;
150         var iea = 0;
151         s.left = (l+a.l)+"px";
152         s.top = (t+a.t)+"px";
153         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
154  
155         if(s.width != sws || s.height != shs){
156             s.width = sws;
157             s.height = shs;
158             if(!Roo.isIE){
159                 var cn = d.childNodes;
160                 var sww = Math.max(0, (sw-12))+"px";
161                 cn[0].childNodes[1].style.width = sww;
162                 cn[1].childNodes[1].style.width = sww;
163                 cn[2].childNodes[1].style.width = sww;
164                 cn[1].style.height = Math.max(0, (sh-12))+"px";
165             }
166         }
167     },
168
169     /**
170      * Hides this shadow
171      */
172     hide : function(){
173         if(this.el){
174             this.el.dom.style.display = "none";
175             Roo.Shadow.Pool.push(this.el);
176             delete this.el;
177         }
178     },
179
180     /**
181      * Adjust the z-index of this shadow
182      * @param {Number} zindex The new z-index
183      */
184     setZIndex : function(z){
185         this.zIndex = z;
186         if(this.el){
187             this.el.setStyle("z-index", z);
188         }
189     }
190 };
191
192 // Private utility class that manages the internal Shadow cache
193 Roo.Shadow.Pool = function(){
194     var p = [];
195     var markup = Roo.isIE ?
196                  '<div class="x-ie-shadow"></div>' :
197                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
198     return {
199         pull : function(){
200             var sh = p.shift();
201             if(!sh){
202                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
203                 sh.autoBoxAdjust = false;
204             }
205             return sh;
206         },
207
208         push : function(sh){
209             p.push(sh);
210         }
211     };
212 }();/*
213  * - LGPL
214  *
215  * base class for bootstrap elements.
216  * 
217  */
218
219 Roo.bootstrap = Roo.bootstrap || {};
220 /**
221  * @class Roo.bootstrap.Component
222  * @extends Roo.Component
223  * Bootstrap Component base class
224  * @cfg {String} cls css class
225  * @cfg {String} style any extra css
226  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
227  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
228  * @cfg {string} dataId cutomer id
229  * @cfg {string} name Specifies name attribute
230  * @cfg {string} tooltip  Text for the tooltip
231  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
232  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
233  
234  * @constructor
235  * Do not use directly - it does not do anything..
236  * @param {Object} config The config object
237  */
238
239
240
241 Roo.bootstrap.Component = function(config){
242     Roo.bootstrap.Component.superclass.constructor.call(this, config);
243        
244     this.addEvents({
245         /**
246          * @event childrenrendered
247          * Fires when the children have been rendered..
248          * @param {Roo.bootstrap.Component} this
249          */
250         "childrenrendered" : true
251         
252         
253         
254     });
255     
256     
257 };
258
259 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
260     
261     
262     allowDomMove : false, // to stop relocations in parent onRender...
263     
264     cls : false,
265     
266     style : false,
267     
268     autoCreate : false,
269     
270     tooltip : null,
271     /**
272      * Initialize Events for the element
273      */
274     initEvents : function() { },
275     
276     xattr : false,
277     
278     parentId : false,
279     
280     can_build_overlaid : true,
281     
282     container_method : false,
283     
284     dataId : false,
285     
286     name : false,
287     
288     parent: function() {
289         // returns the parent component..
290         return Roo.ComponentMgr.get(this.parentId)
291         
292         
293     },
294     
295     // private
296     onRender : function(ct, position)
297     {
298        // Roo.log("Call onRender: " + this.xtype);
299         
300         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
301         
302         if(this.el){
303             if (this.el.attr('xtype')) {
304                 this.el.attr('xtypex', this.el.attr('xtype'));
305                 this.el.dom.removeAttribute('xtype');
306                 
307                 this.initEvents();
308             }
309             
310             return;
311         }
312         
313          
314         
315         var cfg = Roo.apply({},  this.getAutoCreate());
316         
317         cfg.id = this.id || Roo.id();
318         
319         // fill in the extra attributes 
320         if (this.xattr && typeof(this.xattr) =='object') {
321             for (var i in this.xattr) {
322                 cfg[i] = this.xattr[i];
323             }
324         }
325         
326         if(this.dataId){
327             cfg.dataId = this.dataId;
328         }
329         
330         if (this.cls) {
331             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
332         }
333         
334         if (this.style) { // fixme needs to support more complex style data.
335             cfg.style = this.style;
336         }
337         
338         if(this.name){
339             cfg.name = this.name;
340         }
341         
342         this.el = ct.createChild(cfg, position);
343         
344         if (this.tooltip) {
345             this.tooltipEl().attr('tooltip', this.tooltip);
346         }
347         
348         if(this.tabIndex !== undefined){
349             this.el.dom.setAttribute('tabIndex', this.tabIndex);
350         }
351         
352         this.initEvents();
353         
354     },
355     /**
356      * Fetch the element to add children to
357      * @return {Roo.Element} defaults to this.el
358      */
359     getChildContainer : function()
360     {
361         return this.el;
362     },
363     /**
364      * Fetch the element to display the tooltip on.
365      * @return {Roo.Element} defaults to this.el
366      */
367     tooltipEl : function()
368     {
369         return this.el;
370     },
371         
372     addxtype  : function(tree,cntr)
373     {
374         var cn = this;
375         
376         cn = Roo.factory(tree);
377         //Roo.log(['addxtype', cn]);
378            
379         cn.parentType = this.xtype; //??
380         cn.parentId = this.id;
381         
382         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
383         if (typeof(cn.container_method) == 'string') {
384             cntr = cn.container_method;
385         }
386         
387         
388         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
389         
390         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
391         
392         var build_from_html =  Roo.XComponent.build_from_html;
393           
394         var is_body  = (tree.xtype == 'Body') ;
395           
396         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
397           
398         var self_cntr_el = Roo.get(this[cntr](false));
399         
400         // do not try and build conditional elements 
401         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
402             return false;
403         }
404         
405         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
406             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
407                 return this.addxtypeChild(tree,cntr, is_body);
408             }
409             
410             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
411                 
412             if(echild){
413                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
414             }
415             
416             Roo.log('skipping render');
417             return cn;
418             
419         }
420         
421         var ret = false;
422         if (!build_from_html) {
423             return false;
424         }
425         
426         // this i think handles overlaying multiple children of the same type
427         // with the sam eelement.. - which might be buggy..
428         while (true) {
429             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
430             
431             if (!echild) {
432                 break;
433             }
434             
435             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
436                 break;
437             }
438             
439             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
440         }
441        
442         return ret;
443     },
444     
445     
446     addxtypeChild : function (tree, cntr, is_body)
447     {
448         Roo.debug && Roo.log('addxtypeChild:' + cntr);
449         var cn = this;
450         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
451         
452         
453         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
454                     (typeof(tree['flexy:foreach']) != 'undefined');
455           
456     
457         
458         skip_children = false;
459         // render the element if it's not BODY.
460         if (!is_body) {
461             
462             // if parent was disabled, then do not try and create the children..
463             if(!this[cntr](true)){
464                 tree.items = [];
465                 return tree;
466             }
467            
468             cn = Roo.factory(tree);
469            
470             cn.parentType = this.xtype; //??
471             cn.parentId = this.id;
472             
473             var build_from_html =  Roo.XComponent.build_from_html;
474             
475             
476             // does the container contain child eleemnts with 'xtype' attributes.
477             // that match this xtype..
478             // note - when we render we create these as well..
479             // so we should check to see if body has xtype set.
480             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
481                
482                 var self_cntr_el = Roo.get(this[cntr](false));
483                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
484                 if (echild) { 
485                     //Roo.log(Roo.XComponent.build_from_html);
486                     //Roo.log("got echild:");
487                     //Roo.log(echild);
488                 }
489                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
490                 // and are not displayed -this causes this to use up the wrong element when matching.
491                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
492                 
493                 
494                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
495                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
496                   
497                   
498                   
499                     cn.el = echild;
500                   //  Roo.log("GOT");
501                     //echild.dom.removeAttribute('xtype');
502                 } else {
503                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
504                     Roo.debug && Roo.log(self_cntr_el);
505                     Roo.debug && Roo.log(echild);
506                     Roo.debug && Roo.log(cn);
507                 }
508             }
509            
510             
511            
512             // if object has flexy:if - then it may or may not be rendered.
513             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
514                 // skip a flexy if element.
515                 Roo.debug && Roo.log('skipping render');
516                 Roo.debug && Roo.log(tree);
517                 if (!cn.el) {
518                     Roo.debug && Roo.log('skipping all children');
519                     skip_children = true;
520                 }
521                 
522              } else {
523                  
524                 // actually if flexy:foreach is found, we really want to create 
525                 // multiple copies here...
526                 //Roo.log('render');
527                 //Roo.log(this[cntr]());
528                 // some elements do not have render methods.. like the layouts...
529                 /*
530                 if(this[cntr](true) === false){
531                     cn.items = [];
532                     return cn;
533                 }
534                 */
535                 cn.render && cn.render(this[cntr](true));
536                 
537              }
538             // then add the element..
539         }
540          
541         // handle the kids..
542         
543         var nitems = [];
544         /*
545         if (typeof (tree.menu) != 'undefined') {
546             tree.menu.parentType = cn.xtype;
547             tree.menu.triggerEl = cn.el;
548             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
549             
550         }
551         */
552         if (!tree.items || !tree.items.length) {
553             cn.items = nitems;
554             //Roo.log(["no children", this]);
555             
556             return cn;
557         }
558          
559         var items = tree.items;
560         delete tree.items;
561         
562         //Roo.log(items.length);
563             // add the items..
564         if (!skip_children) {    
565             for(var i =0;i < items.length;i++) {
566               //  Roo.log(['add child', items[i]]);
567                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
568             }
569         }
570         
571         cn.items = nitems;
572         
573         //Roo.log("fire childrenrendered");
574         
575         cn.fireEvent('childrenrendered', this);
576         
577         return cn;
578     },
579     
580     /**
581      * Set the element that will be used to show or hide
582      */
583     setVisibilityEl : function(el)
584     {
585         this.visibilityEl = el;
586     },
587     
588      /**
589      * Get the element that will be used to show or hide
590      */
591     getVisibilityEl : function()
592     {
593         if (typeof(this.visibilityEl) == 'object') {
594             return this.visibilityEl;
595         }
596         
597         if (typeof(this.visibilityEl) == 'string') {
598             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
599         }
600         
601         return this.getEl();
602     },
603     
604     /**
605      * Show a component - removes 'hidden' class
606      */
607     show : function()
608     {
609         if(!this.getVisibilityEl()){
610             return;
611         }
612          
613         this.getVisibilityEl().removeClass(['hidden','d-none']);
614         
615         this.fireEvent('show', this);
616         
617         
618     },
619     /**
620      * Hide a component - adds 'hidden' class
621      */
622     hide: function()
623     {
624         if(!this.getVisibilityEl()){
625             return;
626         }
627         
628         this.getVisibilityEl().addClass(['hidden','d-none']);
629         
630         this.fireEvent('hide', this);
631         
632     }
633 });
634
635  /*
636  * - LGPL
637  *
638  * element
639  * 
640  */
641
642 /**
643  * @class Roo.bootstrap.Element
644  * @extends Roo.bootstrap.Component
645  * Bootstrap Element class
646  * @cfg {String} html contents of the element
647  * @cfg {String} tag tag of the element
648  * @cfg {String} cls class of the element
649  * @cfg {Boolean} preventDefault (true|false) default false
650  * @cfg {Boolean} clickable (true|false) default false
651  * 
652  * @constructor
653  * Create a new Element
654  * @param {Object} config The config object
655  */
656
657 Roo.bootstrap.Element = function(config){
658     Roo.bootstrap.Element.superclass.constructor.call(this, config);
659     
660     this.addEvents({
661         // raw events
662         /**
663          * @event click
664          * When a element is chick
665          * @param {Roo.bootstrap.Element} this
666          * @param {Roo.EventObject} e
667          */
668         "click" : true
669     });
670 };
671
672 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
673     
674     tag: 'div',
675     cls: '',
676     html: '',
677     preventDefault: false, 
678     clickable: false,
679     
680     getAutoCreate : function(){
681         
682         var cfg = {
683             tag: this.tag,
684             // cls: this.cls, double assign in parent class Component.js :: onRender
685             html: this.html
686         };
687         
688         return cfg;
689     },
690     
691     initEvents: function() 
692     {
693         Roo.bootstrap.Element.superclass.initEvents.call(this);
694         
695         if(this.clickable){
696             this.el.on('click', this.onClick, this);
697         }
698         
699     },
700     
701     onClick : function(e)
702     {
703         if(this.preventDefault){
704             e.preventDefault();
705         }
706         
707         this.fireEvent('click', this, e);
708     },
709     
710     getValue : function()
711     {
712         return this.el.dom.innerHTML;
713     },
714     
715     setValue : function(value)
716     {
717         this.el.dom.innerHTML = value;
718     }
719    
720 });
721
722  
723
724  /*
725  * - LGPL
726  *
727  * dropable area
728  * 
729  */
730
731 /**
732  * @class Roo.bootstrap.DropTarget
733  * @extends Roo.bootstrap.Element
734  * Bootstrap DropTarget class
735  
736  * @cfg {string} name dropable name
737  * 
738  * @constructor
739  * Create a new Dropable Area
740  * @param {Object} config The config object
741  */
742
743 Roo.bootstrap.DropTarget = function(config){
744     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
745     
746     this.addEvents({
747         // raw events
748         /**
749          * @event click
750          * When a element is chick
751          * @param {Roo.bootstrap.Element} this
752          * @param {Roo.EventObject} e
753          */
754         "drop" : true
755     });
756 };
757
758 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
759     
760     
761     getAutoCreate : function(){
762         
763          
764     },
765     
766     initEvents: function() 
767     {
768         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
769         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
770             ddGroup: this.name,
771             listeners : {
772                 drop : this.dragDrop.createDelegate(this),
773                 enter : this.dragEnter.createDelegate(this),
774                 out : this.dragOut.createDelegate(this),
775                 over : this.dragOver.createDelegate(this)
776             }
777             
778         });
779         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
780     },
781     
782     dragDrop : function(source,e,data)
783     {
784         // user has to decide how to impliment this.
785         Roo.log('drop');
786         Roo.log(this);
787         //this.fireEvent('drop', this, source, e ,data);
788         return false;
789     },
790     
791     dragEnter : function(n, dd, e, data)
792     {
793         // probably want to resize the element to match the dropped element..
794         Roo.log("enter");
795         this.originalSize = this.el.getSize();
796         this.el.setSize( n.el.getSize());
797         this.dropZone.DDM.refreshCache(this.name);
798         Roo.log([n, dd, e, data]);
799     },
800     
801     dragOut : function(value)
802     {
803         // resize back to normal
804         Roo.log("out");
805         this.el.setSize(this.originalSize);
806         this.dropZone.resetConstraints();
807     },
808     
809     dragOver : function()
810     {
811         // ??? do nothing?
812     }
813    
814 });
815
816  
817
818  /*
819  * - LGPL
820  *
821  * Body
822  *
823  */
824
825 /**
826  * @class Roo.bootstrap.Body
827  * @extends Roo.bootstrap.Component
828  * Bootstrap Body class
829  *
830  * @constructor
831  * Create a new body
832  * @param {Object} config The config object
833  */
834
835 Roo.bootstrap.Body = function(config){
836
837     config = config || {};
838
839     Roo.bootstrap.Body.superclass.constructor.call(this, config);
840     this.el = Roo.get(config.el ? config.el : document.body );
841     if (this.cls && this.cls.length) {
842         Roo.get(document.body).addClass(this.cls);
843     }
844 };
845
846 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
847
848     is_body : true,// just to make sure it's constructed?
849
850         autoCreate : {
851         cls: 'container'
852     },
853     onRender : function(ct, position)
854     {
855        /* Roo.log("Roo.bootstrap.Body - onRender");
856         if (this.cls && this.cls.length) {
857             Roo.get(document.body).addClass(this.cls);
858         }
859         // style??? xttr???
860         */
861     }
862
863
864
865
866 });
867 /*
868  * - LGPL
869  *
870  * button group
871  * 
872  */
873
874
875 /**
876  * @class Roo.bootstrap.ButtonGroup
877  * @extends Roo.bootstrap.Component
878  * Bootstrap ButtonGroup class
879  * @cfg {String} size lg | sm | xs (default empty normal)
880  * @cfg {String} align vertical | justified  (default none)
881  * @cfg {String} direction up | down (default down)
882  * @cfg {Boolean} toolbar false | true
883  * @cfg {Boolean} btn true | false
884  * 
885  * 
886  * @constructor
887  * Create a new Input
888  * @param {Object} config The config object
889  */
890
891 Roo.bootstrap.ButtonGroup = function(config){
892     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
893 };
894
895 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
896     
897     size: '',
898     align: '',
899     direction: '',
900     toolbar: false,
901     btn: true,
902
903     getAutoCreate : function(){
904         var cfg = {
905             cls: 'btn-group',
906             html : null
907         };
908         
909         cfg.html = this.html || cfg.html;
910         
911         if (this.toolbar) {
912             cfg = {
913                 cls: 'btn-toolbar',
914                 html: null
915             };
916             
917             return cfg;
918         }
919         
920         if (['vertical','justified'].indexOf(this.align)!==-1) {
921             cfg.cls = 'btn-group-' + this.align;
922             
923             if (this.align == 'justified') {
924                 console.log(this.items);
925             }
926         }
927         
928         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
929             cfg.cls += ' btn-group-' + this.size;
930         }
931         
932         if (this.direction == 'up') {
933             cfg.cls += ' dropup' ;
934         }
935         
936         return cfg;
937     },
938     /**
939      * Add a button to the group (similar to NavItem API.)
940      */
941     addItem : function(cfg)
942     {
943         var cn = new Roo.bootstrap.Button(cfg);
944         //this.register(cn);
945         cn.parentId = this.id;
946         cn.onRender(this.el, null);
947         return cn;
948     }
949    
950 });
951
952  /*
953  * - LGPL
954  *
955  * button
956  * 
957  */
958
959 /**
960  * @class Roo.bootstrap.Button
961  * @extends Roo.bootstrap.Component
962  * Bootstrap Button class
963  * @cfg {String} html The button content
964  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
965  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
966  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
967  * @cfg {String} size (lg|sm|xs)
968  * @cfg {String} tag (a|input|submit)
969  * @cfg {String} href empty or href
970  * @cfg {Boolean} disabled default false;
971  * @cfg {Boolean} isClose default false;
972  * @cfg {String} glyphicon depricated - use fa
973  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
974  * @cfg {String} badge text for badge
975  * @cfg {String} theme (default|glow)  
976  * @cfg {Boolean} inverse dark themed version
977  * @cfg {Boolean} toggle is it a slidy toggle button
978  * @cfg {Boolean} pressed   default null - if the button ahs active state
979  * @cfg {String} ontext text for on slidy toggle state
980  * @cfg {String} offtext text for off slidy toggle state
981  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
982  * @cfg {Boolean} removeClass remove the standard class..
983  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
984  * 
985  * @constructor
986  * Create a new button
987  * @param {Object} config The config object
988  */
989
990
991 Roo.bootstrap.Button = function(config){
992     Roo.bootstrap.Button.superclass.constructor.call(this, config);
993     
994     this.addEvents({
995         // raw events
996         /**
997          * @event click
998          * When a butotn is pressed
999          * @param {Roo.bootstrap.Button} btn
1000          * @param {Roo.EventObject} e
1001          */
1002         "click" : true,
1003          /**
1004          * @event toggle
1005          * After the button has been toggles
1006          * @param {Roo.bootstrap.Button} btn
1007          * @param {Roo.EventObject} e
1008          * @param {boolean} pressed (also available as button.pressed)
1009          */
1010         "toggle" : true
1011     });
1012 };
1013
1014 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1015     html: false,
1016     active: false,
1017     weight: '',
1018     badge_weight: '',
1019     outline : false,
1020     size: '',
1021     tag: 'button',
1022     href: '',
1023     disabled: false,
1024     isClose: false,
1025     glyphicon: '',
1026     fa: '',
1027     badge: '',
1028     theme: 'default',
1029     inverse: false,
1030     
1031     toggle: false,
1032     ontext: 'ON',
1033     offtext: 'OFF',
1034     defaulton: true,
1035     preventDefault: true,
1036     removeClass: false,
1037     name: false,
1038     target: false,
1039      
1040     pressed : null,
1041      
1042     
1043     getAutoCreate : function(){
1044         
1045         var cfg = {
1046             tag : 'button',
1047             cls : 'roo-button',
1048             html: ''
1049         };
1050         
1051         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1052             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1053             this.tag = 'button';
1054         } else {
1055             cfg.tag = this.tag;
1056         }
1057         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1058         
1059         if (this.toggle == true) {
1060             cfg={
1061                 tag: 'div',
1062                 cls: 'slider-frame roo-button',
1063                 cn: [
1064                     {
1065                         tag: 'span',
1066                         'data-on-text':'ON',
1067                         'data-off-text':'OFF',
1068                         cls: 'slider-button',
1069                         html: this.offtext
1070                     }
1071                 ]
1072             };
1073             // why are we validating the weights?
1074             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1075                 cfg.cls +=  ' ' + this.weight;
1076             }
1077             
1078             return cfg;
1079         }
1080         
1081         if (this.isClose) {
1082             cfg.cls += ' close';
1083             
1084             cfg["aria-hidden"] = true;
1085             
1086             cfg.html = "&times;";
1087             
1088             return cfg;
1089         }
1090              
1091         
1092         if (this.theme==='default') {
1093             cfg.cls = 'btn roo-button';
1094             
1095             //if (this.parentType != 'Navbar') {
1096             this.weight = this.weight.length ?  this.weight : 'default';
1097             //}
1098             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1099                 
1100                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1101                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1102                 cfg.cls += ' btn-' + outline + weight;
1103                 if (this.weight == 'default') {
1104                     // BC
1105                     cfg.cls += ' btn-' + this.weight;
1106                 }
1107             }
1108         } else if (this.theme==='glow') {
1109             
1110             cfg.tag = 'a';
1111             cfg.cls = 'btn-glow roo-button';
1112             
1113             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1114                 
1115                 cfg.cls += ' ' + this.weight;
1116             }
1117         }
1118    
1119         
1120         if (this.inverse) {
1121             this.cls += ' inverse';
1122         }
1123         
1124         
1125         if (this.active || this.pressed === true) {
1126             cfg.cls += ' active';
1127         }
1128         
1129         if (this.disabled) {
1130             cfg.disabled = 'disabled';
1131         }
1132         
1133         if (this.items) {
1134             Roo.log('changing to ul' );
1135             cfg.tag = 'ul';
1136             this.glyphicon = 'caret';
1137             if (Roo.bootstrap.version == 4) {
1138                 this.fa = 'caret-down';
1139             }
1140             
1141         }
1142         
1143         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1144          
1145         //gsRoo.log(this.parentType);
1146         if (this.parentType === 'Navbar' && !this.parent().bar) {
1147             Roo.log('changing to li?');
1148             
1149             cfg.tag = 'li';
1150             
1151             cfg.cls = '';
1152             cfg.cn =  [{
1153                 tag : 'a',
1154                 cls : 'roo-button',
1155                 html : this.html,
1156                 href : this.href || '#'
1157             }];
1158             if (this.menu) {
1159                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1160                 cfg.cls += ' dropdown';
1161             }   
1162             
1163             delete cfg.html;
1164             
1165         }
1166         
1167        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1168         
1169         if (this.glyphicon) {
1170             cfg.html = ' ' + cfg.html;
1171             
1172             cfg.cn = [
1173                 {
1174                     tag: 'span',
1175                     cls: 'glyphicon glyphicon-' + this.glyphicon
1176                 }
1177             ];
1178         }
1179         if (this.fa) {
1180             cfg.html = ' ' + cfg.html;
1181             
1182             cfg.cn = [
1183                 {
1184                     tag: 'i',
1185                     cls: 'fa fas fa-' + this.fa
1186                 }
1187             ];
1188         }
1189         
1190         if (this.badge) {
1191             cfg.html += ' ';
1192             
1193             cfg.tag = 'a';
1194             
1195 //            cfg.cls='btn roo-button';
1196             
1197             cfg.href=this.href;
1198             
1199             var value = cfg.html;
1200             
1201             if(this.glyphicon){
1202                 value = {
1203                     tag: 'span',
1204                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1205                     html: this.html
1206                 };
1207             }
1208             if(this.fa){
1209                 value = {
1210                     tag: 'i',
1211                     cls: 'fa fas fa-' + this.fa,
1212                     html: this.html
1213                 };
1214             }
1215             
1216             var bw = this.badge_weight.length ? this.badge_weight :
1217                 (this.weight.length ? this.weight : 'secondary');
1218             bw = bw == 'default' ? 'secondary' : bw;
1219             
1220             cfg.cn = [
1221                 value,
1222                 {
1223                     tag: 'span',
1224                     cls: 'badge badge-' + bw,
1225                     html: this.badge
1226                 }
1227             ];
1228             
1229             cfg.html='';
1230         }
1231         
1232         if (this.menu) {
1233             cfg.cls += ' dropdown';
1234             cfg.html = typeof(cfg.html) != 'undefined' ?
1235                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1236         }
1237         
1238         if (cfg.tag !== 'a' && this.href !== '') {
1239             throw "Tag must be a to set href.";
1240         } else if (this.href.length > 0) {
1241             cfg.href = this.href;
1242         }
1243         
1244         if(this.removeClass){
1245             cfg.cls = '';
1246         }
1247         
1248         if(this.target){
1249             cfg.target = this.target;
1250         }
1251         
1252         return cfg;
1253     },
1254     initEvents: function() {
1255        // Roo.log('init events?');
1256 //        Roo.log(this.el.dom);
1257         // add the menu...
1258         
1259         if (typeof (this.menu) != 'undefined') {
1260             this.menu.parentType = this.xtype;
1261             this.menu.triggerEl = this.el;
1262             this.addxtype(Roo.apply({}, this.menu));
1263         }
1264
1265
1266        if (this.el.hasClass('roo-button')) {
1267             this.el.on('click', this.onClick, this);
1268        } else {
1269             this.el.select('.roo-button').on('click', this.onClick, this);
1270        }
1271        
1272        if(this.removeClass){
1273            this.el.on('click', this.onClick, this);
1274        }
1275        
1276        this.el.enableDisplayMode();
1277         
1278     },
1279     onClick : function(e)
1280     {
1281         if (this.disabled) {
1282             return;
1283         }
1284         
1285         Roo.log('button on click ');
1286         if(this.preventDefault){
1287             e.preventDefault();
1288         }
1289         
1290         if (this.pressed === true || this.pressed === false) {
1291             this.toggleActive(e);
1292         }
1293         
1294         
1295         this.fireEvent('click', this, e);
1296     },
1297     
1298     /**
1299      * Enables this button
1300      */
1301     enable : function()
1302     {
1303         this.disabled = false;
1304         this.el.removeClass('disabled');
1305     },
1306     
1307     /**
1308      * Disable this button
1309      */
1310     disable : function()
1311     {
1312         this.disabled = true;
1313         this.el.addClass('disabled');
1314     },
1315      /**
1316      * sets the active state on/off, 
1317      * @param {Boolean} state (optional) Force a particular state
1318      */
1319     setActive : function(v) {
1320         
1321         this.el[v ? 'addClass' : 'removeClass']('active');
1322         this.pressed = v;
1323     },
1324      /**
1325      * toggles the current active state 
1326      */
1327     toggleActive : function(e)
1328     {
1329         this.setActive(!this.pressed); // this modifies pressed...
1330         this.fireEvent('toggle', this, e, this.pressed);
1331     },
1332      /**
1333      * get the current active state
1334      * @return {boolean} true if it's active
1335      */
1336     isActive : function()
1337     {
1338         return this.el.hasClass('active');
1339     },
1340     /**
1341      * set the text of the first selected button
1342      */
1343     setText : function(str)
1344     {
1345         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1346     },
1347     /**
1348      * get the text of the first selected button
1349      */
1350     getText : function()
1351     {
1352         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1353     },
1354     
1355     setWeight : function(str)
1356     {
1357         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1358         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1359         this.weight = str;
1360         var outline = this.outline ? 'outline-' : '';
1361         if (str == 'default') {
1362             this.el.addClass('btn-default btn-outline-secondary');        
1363             return;
1364         }
1365         this.el.addClass('btn-' + outline + str);        
1366     }
1367     
1368     
1369 });
1370 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1371
1372 Roo.bootstrap.Button.weights = [
1373     'default',
1374     'secondary' ,
1375     'primary',
1376     'success',
1377     'info',
1378     'warning',
1379     'danger',
1380     'link',
1381     'light',
1382     'dark'              
1383    
1384 ];/*
1385  * - LGPL
1386  *
1387  * column
1388  * 
1389  */
1390
1391 /**
1392  * @class Roo.bootstrap.Column
1393  * @extends Roo.bootstrap.Component
1394  * Bootstrap Column class
1395  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1396  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1397  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1398  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1399  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1400  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1401  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1402  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1403  *
1404  * 
1405  * @cfg {Boolean} hidden (true|false) hide the element
1406  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1407  * @cfg {String} fa (ban|check|...) font awesome icon
1408  * @cfg {Number} fasize (1|2|....) font awsome size
1409
1410  * @cfg {String} icon (info-sign|check|...) glyphicon name
1411
1412  * @cfg {String} html content of column.
1413  * 
1414  * @constructor
1415  * Create a new Column
1416  * @param {Object} config The config object
1417  */
1418
1419 Roo.bootstrap.Column = function(config){
1420     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1421 };
1422
1423 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1424     
1425     xs: false,
1426     sm: false,
1427     md: false,
1428     lg: false,
1429     xsoff: false,
1430     smoff: false,
1431     mdoff: false,
1432     lgoff: false,
1433     html: '',
1434     offset: 0,
1435     alert: false,
1436     fa: false,
1437     icon : false,
1438     hidden : false,
1439     fasize : 1,
1440     
1441     getAutoCreate : function(){
1442         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1443         
1444         cfg = {
1445             tag: 'div',
1446             cls: 'column'
1447         };
1448         
1449         var settings=this;
1450         var sizes =   ['xs','sm','md','lg'];
1451         sizes.map(function(size ,ix){
1452             //Roo.log( size + ':' + settings[size]);
1453             
1454             if (settings[size+'off'] !== false) {
1455                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1456             }
1457             
1458             if (settings[size] === false) {
1459                 return;
1460             }
1461             
1462             if (!settings[size]) { // 0 = hidden
1463                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1464                 // bootsrap4
1465                 for (var i = ix; i > -1; i--) {
1466                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1467                 }
1468                 
1469                 
1470                 return;
1471             }
1472             cfg.cls += ' col-' + size + '-' + settings[size] + (
1473                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1474             );
1475             
1476         });
1477         
1478         if (this.hidden) {
1479             cfg.cls += ' hidden';
1480         }
1481         
1482         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1483             cfg.cls +=' alert alert-' + this.alert;
1484         }
1485         
1486         
1487         if (this.html.length) {
1488             cfg.html = this.html;
1489         }
1490         if (this.fa) {
1491             var fasize = '';
1492             if (this.fasize > 1) {
1493                 fasize = ' fa-' + this.fasize + 'x';
1494             }
1495             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1496             
1497             
1498         }
1499         if (this.icon) {
1500             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1501         }
1502         
1503         return cfg;
1504     }
1505    
1506 });
1507
1508  
1509
1510  /*
1511  * - LGPL
1512  *
1513  * page container.
1514  * 
1515  */
1516
1517
1518 /**
1519  * @class Roo.bootstrap.Container
1520  * @extends Roo.bootstrap.Component
1521  * Bootstrap Container class
1522  * @cfg {Boolean} jumbotron is it a jumbotron element
1523  * @cfg {String} html content of element
1524  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1525  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1526  * @cfg {String} header content of header (for panel)
1527  * @cfg {String} footer content of footer (for panel)
1528  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1529  * @cfg {String} tag (header|aside|section) type of HTML tag.
1530  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1531  * @cfg {String} fa font awesome icon
1532  * @cfg {String} icon (info-sign|check|...) glyphicon name
1533  * @cfg {Boolean} hidden (true|false) hide the element
1534  * @cfg {Boolean} expandable (true|false) default false
1535  * @cfg {Boolean} expanded (true|false) default true
1536  * @cfg {String} rheader contet on the right of header
1537  * @cfg {Boolean} clickable (true|false) default false
1538
1539  *     
1540  * @constructor
1541  * Create a new Container
1542  * @param {Object} config The config object
1543  */
1544
1545 Roo.bootstrap.Container = function(config){
1546     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1547     
1548     this.addEvents({
1549         // raw events
1550          /**
1551          * @event expand
1552          * After the panel has been expand
1553          * 
1554          * @param {Roo.bootstrap.Container} this
1555          */
1556         "expand" : true,
1557         /**
1558          * @event collapse
1559          * After the panel has been collapsed
1560          * 
1561          * @param {Roo.bootstrap.Container} this
1562          */
1563         "collapse" : true,
1564         /**
1565          * @event click
1566          * When a element is chick
1567          * @param {Roo.bootstrap.Container} this
1568          * @param {Roo.EventObject} e
1569          */
1570         "click" : true
1571     });
1572 };
1573
1574 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1575     
1576     jumbotron : false,
1577     well: '',
1578     panel : '',
1579     header: '',
1580     footer : '',
1581     sticky: '',
1582     tag : false,
1583     alert : false,
1584     fa: false,
1585     icon : false,
1586     expandable : false,
1587     rheader : '',
1588     expanded : true,
1589     clickable: false,
1590   
1591      
1592     getChildContainer : function() {
1593         
1594         if(!this.el){
1595             return false;
1596         }
1597         
1598         if (this.panel.length) {
1599             return this.el.select('.panel-body',true).first();
1600         }
1601         
1602         return this.el;
1603     },
1604     
1605     
1606     getAutoCreate : function(){
1607         
1608         var cfg = {
1609             tag : this.tag || 'div',
1610             html : '',
1611             cls : ''
1612         };
1613         if (this.jumbotron) {
1614             cfg.cls = 'jumbotron';
1615         }
1616         
1617         
1618         
1619         // - this is applied by the parent..
1620         //if (this.cls) {
1621         //    cfg.cls = this.cls + '';
1622         //}
1623         
1624         if (this.sticky.length) {
1625             
1626             var bd = Roo.get(document.body);
1627             if (!bd.hasClass('bootstrap-sticky')) {
1628                 bd.addClass('bootstrap-sticky');
1629                 Roo.select('html',true).setStyle('height', '100%');
1630             }
1631              
1632             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1633         }
1634         
1635         
1636         if (this.well.length) {
1637             switch (this.well) {
1638                 case 'lg':
1639                 case 'sm':
1640                     cfg.cls +=' well well-' +this.well;
1641                     break;
1642                 default:
1643                     cfg.cls +=' well';
1644                     break;
1645             }
1646         }
1647         
1648         if (this.hidden) {
1649             cfg.cls += ' hidden';
1650         }
1651         
1652         
1653         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1654             cfg.cls +=' alert alert-' + this.alert;
1655         }
1656         
1657         var body = cfg;
1658         
1659         if (this.panel.length) {
1660             cfg.cls += ' panel panel-' + this.panel;
1661             cfg.cn = [];
1662             if (this.header.length) {
1663                 
1664                 var h = [];
1665                 
1666                 if(this.expandable){
1667                     
1668                     cfg.cls = cfg.cls + ' expandable';
1669                     
1670                     h.push({
1671                         tag: 'i',
1672                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1673                     });
1674                     
1675                 }
1676                 
1677                 h.push(
1678                     {
1679                         tag: 'span',
1680                         cls : 'panel-title',
1681                         html : (this.expandable ? '&nbsp;' : '') + this.header
1682                     },
1683                     {
1684                         tag: 'span',
1685                         cls: 'panel-header-right',
1686                         html: this.rheader
1687                     }
1688                 );
1689                 
1690                 cfg.cn.push({
1691                     cls : 'panel-heading',
1692                     style : this.expandable ? 'cursor: pointer' : '',
1693                     cn : h
1694                 });
1695                 
1696             }
1697             
1698             body = false;
1699             cfg.cn.push({
1700                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1701                 html : this.html
1702             });
1703             
1704             
1705             if (this.footer.length) {
1706                 cfg.cn.push({
1707                     cls : 'panel-footer',
1708                     html : this.footer
1709                     
1710                 });
1711             }
1712             
1713         }
1714         
1715         if (body) {
1716             body.html = this.html || cfg.html;
1717             // prefix with the icons..
1718             if (this.fa) {
1719                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1720             }
1721             if (this.icon) {
1722                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1723             }
1724             
1725             
1726         }
1727         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1728             cfg.cls =  'container';
1729         }
1730         
1731         return cfg;
1732     },
1733     
1734     initEvents: function() 
1735     {
1736         if(this.expandable){
1737             var headerEl = this.headerEl();
1738         
1739             if(headerEl){
1740                 headerEl.on('click', this.onToggleClick, this);
1741             }
1742         }
1743         
1744         if(this.clickable){
1745             this.el.on('click', this.onClick, this);
1746         }
1747         
1748     },
1749     
1750     onToggleClick : function()
1751     {
1752         var headerEl = this.headerEl();
1753         
1754         if(!headerEl){
1755             return;
1756         }
1757         
1758         if(this.expanded){
1759             this.collapse();
1760             return;
1761         }
1762         
1763         this.expand();
1764     },
1765     
1766     expand : function()
1767     {
1768         if(this.fireEvent('expand', this)) {
1769             
1770             this.expanded = true;
1771             
1772             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1773             
1774             this.el.select('.panel-body',true).first().removeClass('hide');
1775             
1776             var toggleEl = this.toggleEl();
1777
1778             if(!toggleEl){
1779                 return;
1780             }
1781
1782             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1783         }
1784         
1785     },
1786     
1787     collapse : function()
1788     {
1789         if(this.fireEvent('collapse', this)) {
1790             
1791             this.expanded = false;
1792             
1793             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1794             this.el.select('.panel-body',true).first().addClass('hide');
1795         
1796             var toggleEl = this.toggleEl();
1797
1798             if(!toggleEl){
1799                 return;
1800             }
1801
1802             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1803         }
1804     },
1805     
1806     toggleEl : function()
1807     {
1808         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1809             return;
1810         }
1811         
1812         return this.el.select('.panel-heading .fa',true).first();
1813     },
1814     
1815     headerEl : function()
1816     {
1817         if(!this.el || !this.panel.length || !this.header.length){
1818             return;
1819         }
1820         
1821         return this.el.select('.panel-heading',true).first()
1822     },
1823     
1824     bodyEl : function()
1825     {
1826         if(!this.el || !this.panel.length){
1827             return;
1828         }
1829         
1830         return this.el.select('.panel-body',true).first()
1831     },
1832     
1833     titleEl : function()
1834     {
1835         if(!this.el || !this.panel.length || !this.header.length){
1836             return;
1837         }
1838         
1839         return this.el.select('.panel-title',true).first();
1840     },
1841     
1842     setTitle : function(v)
1843     {
1844         var titleEl = this.titleEl();
1845         
1846         if(!titleEl){
1847             return;
1848         }
1849         
1850         titleEl.dom.innerHTML = v;
1851     },
1852     
1853     getTitle : function()
1854     {
1855         
1856         var titleEl = this.titleEl();
1857         
1858         if(!titleEl){
1859             return '';
1860         }
1861         
1862         return titleEl.dom.innerHTML;
1863     },
1864     
1865     setRightTitle : function(v)
1866     {
1867         var t = this.el.select('.panel-header-right',true).first();
1868         
1869         if(!t){
1870             return;
1871         }
1872         
1873         t.dom.innerHTML = v;
1874     },
1875     
1876     onClick : function(e)
1877     {
1878         e.preventDefault();
1879         
1880         this.fireEvent('click', this, e);
1881     }
1882 });
1883
1884  /*
1885  *  - LGPL
1886  *
1887  *  This is BS4's Card element.. - similar to our containers probably..
1888  * 
1889  */
1890 /**
1891  * @class Roo.bootstrap.Card
1892  * @extends Roo.bootstrap.Component
1893  * Bootstrap Card class
1894  *
1895  *
1896  * possible... may not be implemented..
1897  * @cfg {String} header_image  src url of image.
1898  * @cfg {String|Object} header
1899  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1900  * 
1901  * @cfg {String} title
1902  * @cfg {String} subtitle
1903  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1904  * @cfg {String} footer
1905  
1906  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1907  * 
1908  * @cfg {String} margin (0|1|2|3|4|5|auto)
1909  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1910  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1911  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1912  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1913  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1914  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1915  *
1916  * @cfg {String} padding (0|1|2|3|4|5)
1917  * @cfg {String} padding_top (0|1|2|3|4|5)
1918  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1919  * @cfg {String} padding_left (0|1|2|3|4|5)
1920  * @cfg {String} padding_right (0|1|2|3|4|5)
1921  * @cfg {String} padding_x (0|1|2|3|4|5)
1922  * @cfg {String} padding_y (0|1|2|3|4|5)
1923  *
1924  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1925  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1926  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1929  
1930  * @config {Boolean} dragable  if this card can be dragged.
1931  * @config {String} drag_group  group for drag
1932  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1933  * @config {String} drop_group  group for drag
1934  * 
1935  * @config {Boolean} collapsable can the body be collapsed.
1936  * @config {Boolean} collapsed is the body collapsed when rendered...
1937  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1938  * @config {Boolean} rotated is the body rotated when rendered...
1939  * 
1940  * @constructor
1941  * Create a new Container
1942  * @param {Object} config The config object
1943  */
1944
1945 Roo.bootstrap.Card = function(config){
1946     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1947     
1948     this.addEvents({
1949          // raw events
1950         /**
1951          * @event drop
1952          * When a element a card is dropped
1953          * @param {Roo.bootstrap.Card} this
1954          *
1955          * 
1956          * @param {Roo.bootstrap.Card} move_card the card being dropped?
1957          * @param {String} position 'above' or 'below'
1958          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
1959         
1960          */
1961         'drop' : true,
1962          /**
1963          * @event rotate
1964          * When a element a card is rotate
1965          * @param {Roo.bootstrap.Element} this
1966          * @param {Roo.Element} n the node being dropped?
1967          * @param {Boolean} rotate status
1968          */
1969         'rotate' : true
1970         
1971     });
1972 };
1973
1974
1975 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1976     
1977     
1978     weight : '',
1979     
1980     margin: '', /// may be better in component?
1981     margin_top: '', 
1982     margin_bottom: '', 
1983     margin_left: '',
1984     margin_right: '',
1985     margin_x: '',
1986     margin_y: '',
1987     
1988     padding : '',
1989     padding_top: '', 
1990     padding_bottom: '', 
1991     padding_left: '',
1992     padding_right: '',
1993     padding_x: '',
1994     padding_y: '',
1995     
1996     display: '', 
1997     display_xs: '', 
1998     display_sm: '', 
1999     display_lg: '',
2000     display_xl: '',
2001  
2002     header_image  : '',
2003     header : '',
2004     header_size : 0,
2005     title : '',
2006     subtitle : '',
2007     html : '',
2008     footer: '',
2009
2010     collapsable : false,
2011     collapsed : false,
2012     rotateable : false,
2013     rotated : false,
2014     
2015     dragable : false,
2016     drag_group : false,
2017     dropable : false,
2018     drop_group : false,
2019     childContainer : false,
2020     dropEl : false, /// the dom placeholde element that indicates drop location.
2021     
2022     layoutCls : function()
2023     {
2024         var cls = '';
2025         var t = this;
2026         Roo.log(this.margin_bottom.length);
2027         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2028             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2029             
2030             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2031                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2032             }
2033             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2034                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2035             }
2036         });
2037         
2038         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2039             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2040                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2041             }
2042         });
2043         
2044         // more generic support?
2045         if (this.hidden) {
2046             cls += ' d-none';
2047         }
2048         
2049         return cls;
2050     },
2051  
2052        // Roo.log("Call onRender: " + this.xtype);
2053         /*  We are looking at something like this.
2054 <div class="card">
2055     <img src="..." class="card-img-top" alt="...">
2056     <div class="card-body">
2057         <h5 class="card-title">Card title</h5>
2058          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2059
2060         >> this bit is really the body...
2061         <div> << we will ad dthis in hopefully it will not break shit.
2062         
2063         ** card text does not actually have any styling...
2064         
2065             <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>
2066         
2067         </div> <<
2068           <a href="#" class="card-link">Card link</a>
2069           
2070     </div>
2071     <div class="card-footer">
2072         <small class="text-muted">Last updated 3 mins ago</small>
2073     </div>
2074 </div>
2075          */
2076     getAutoCreate : function(){
2077         
2078         var cfg = {
2079             tag : 'div',
2080             cls : 'card',
2081             cn : [ ]
2082         };
2083         
2084         if (this.weight.length && this.weight != 'light') {
2085             cfg.cls += ' text-white';
2086         } else {
2087             cfg.cls += ' text-dark'; // need as it's nested..
2088         }
2089         if (this.weight.length) {
2090             cfg.cls += ' bg-' + this.weight;
2091         }
2092         
2093         cfg.cls += this.layoutCls(); 
2094         
2095         var hdr = false;
2096         var hdr_ctr = false;
2097         if (this.header.length) {
2098             hdr = {
2099                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2100                 cls : 'card-header',
2101                 cn : []
2102             };
2103             cfg.cn.push(hdr);
2104             hdr_ctr = hdr;
2105         } else {
2106             hdr = {
2107                 tag : 'div',
2108                 cls : 'card-header d-none',
2109                 cn : []
2110             };
2111             cfg.cn.push(hdr);
2112             hdr_ctr = hdr;
2113         }
2114         if (this.collapsable) {
2115             hdr_ctr = {
2116             tag : 'a',
2117             cls : 'd-block user-select-none',
2118             cn: [
2119                     {
2120                         tag: 'i',
2121                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2122                     }
2123                    
2124                 ]
2125             };
2126             hdr.cn.push(hdr_ctr);
2127         }
2128         
2129         hdr_ctr.cn.push(        {
2130             tag: 'span',
2131             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2132             html : this.header
2133         });
2134         
2135         
2136         if (this.header_image.length) {
2137             cfg.cn.push({
2138                 tag : 'img',
2139                 cls : 'card-img-top',
2140                 src: this.header_image // escape?
2141             });
2142         } else {
2143             cfg.cn.push({
2144                     tag : 'div',
2145                     cls : 'card-img-top d-none' 
2146                 });
2147         }
2148             
2149         var body = {
2150             tag : 'div',
2151             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2152             cn : []
2153         };
2154         var obody = body;
2155         if (this.collapsable || this.rotateable) {
2156             obody = {
2157                 tag: 'div',
2158                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2159                 cn : [  body ]
2160             };
2161         }
2162         
2163         cfg.cn.push(obody);
2164         
2165         if (this.title.length) {
2166             body.cn.push({
2167                 tag : 'div',
2168                 cls : 'card-title',
2169                 src: this.title // escape?
2170             });
2171         }  
2172         
2173         if (this.subtitle.length) {
2174             body.cn.push({
2175                 tag : 'div',
2176                 cls : 'card-title',
2177                 src: this.subtitle // escape?
2178             });
2179         }
2180         
2181         body.cn.push({
2182             tag : 'div',
2183             cls : 'roo-card-body-ctr'
2184         });
2185         
2186         if (this.html.length) {
2187             body.cn.push({
2188                 tag: 'div',
2189                 html : this.html
2190             });
2191         }
2192         // fixme ? handle objects?
2193         
2194         if (this.footer.length) {
2195            
2196             cfg.cn.push({
2197                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2198                 html : this.footer
2199             });
2200             
2201         } else {
2202             cfg.cn.push({cls : 'card-footer d-none'});
2203         }
2204         
2205         // footer...
2206         
2207         return cfg;
2208     },
2209     
2210     
2211     getCardHeader : function()
2212     {
2213         var  ret = this.el.select('.card-header',true).first();
2214         if (ret.hasClass('d-none')) {
2215             ret.removeClass('d-none');
2216         }
2217         
2218         return ret;
2219     },
2220     getCardFooter : function()
2221     {
2222         var  ret = this.el.select('.card-footer',true).first();
2223         if (ret.hasClass('d-none')) {
2224             ret.removeClass('d-none');
2225         }
2226         
2227         return ret;
2228     },
2229     getCardImageTop : function()
2230     {
2231         var  ret = this.el.select('.card-img-top',true).first();
2232         if (ret.hasClass('d-none')) {
2233             ret.removeClass('d-none');
2234         }
2235             
2236         return ret;
2237     },
2238     
2239     getChildContainer : function()
2240     {
2241         
2242         if(!this.el){
2243             return false;
2244         }
2245         return this.el.select('.roo-card-body-ctr',true).first();    
2246     },
2247     
2248     initEvents: function() 
2249     {
2250         
2251         this.bodyEl = this.getChildContainer();
2252         if(this.dragable){
2253             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2254                     containerScroll: true,
2255                     ddGroup: this.drag_group || 'default_card_drag_group'
2256             });
2257             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2258         }
2259         if (this.dropable) {
2260             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2261                 containerScroll: true,
2262                 ddGroup: this.drop_group || 'default_card_drag_group'
2263             });
2264             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2265             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2266             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2267             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2268             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2269         }
2270         
2271         if (this.collapsable) {
2272             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2273         }
2274         if (this.rotateable) {
2275             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2276         }
2277         this.collapsableEl = this.el.select('.roo-collapsable').first();
2278          
2279         this.footerEl = this.el.select('.card-footer').first();
2280         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2281         this.headerEl = this.el.select('.roo-card-header-ctr').first();
2282         
2283         if (this.rotated) {
2284             this.el.addClass('roo-card-rotated');
2285             this.fireEvent('rotate', this, true);
2286         }
2287         
2288     },
2289     getDragData : function(e)
2290     {
2291         var target = this.getEl();
2292         if (target) {
2293             //this.handleSelection(e);
2294             
2295             var dragData = {
2296                 source: this,
2297                 copy: false,
2298                 nodes: this.getEl(),
2299                 records: []
2300             };
2301             
2302             
2303             dragData.ddel = target.dom ;    // the div element
2304             Roo.log(target.getWidth( ));
2305             dragData.ddel.style.width = target.getWidth() + 'px';
2306             
2307             return dragData;
2308         }
2309         return false;
2310     },
2311     /**
2312     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2313     *    whole Element becomes the target, and this causes the drop gesture to append.
2314     */
2315     getTargetFromEvent : function(e, dragged_card_el)
2316     {
2317         var target = e.getTarget();
2318         while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
2319             target = target.parentNode;
2320         }
2321         
2322         var ret = {
2323             position: '',
2324             cards : [],
2325             card_n : -1,
2326             items_n : -1,
2327             card : false 
2328         };
2329         
2330         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2331         // see if target is one of the 'cards'...
2332         
2333         
2334         //Roo.log(this.items.length);
2335         var pos = false;
2336         
2337         var last_card_n = 0;
2338         var cards_len  = 0;
2339         for (var i = 0;i< this.items.length;i++) {
2340             
2341             if (!this.items[i].el.hasClass('card')) {
2342                  continue;
2343             }
2344             pos = this.getDropPoint(e, this.items[i].el.dom);
2345             
2346             cards_len = ret.cards.length;
2347             //Roo.log(this.items[i].el.dom.id);
2348             ret.cards.push(this.items[i]);
2349             last_card_n  = i;
2350             if (ret.card_n < 0 && pos == 'above') {
2351                 ret.position = cards_len > 0 ? 'below' : pos;
2352                 ret.items_n = i > 0 ? i - 1 : 0;
2353                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2354                 ret.card = ret.cards[ret.card_n];
2355             }
2356         }
2357         if (!ret.cards.length) {
2358             ret.card = true;
2359             ret.position = 'below';
2360             ret.items_n;
2361             return ret;
2362         }
2363         // could not find a card.. stick it at the end..
2364         if (ret.card_n < 0) {
2365             ret.card_n = last_card_n;
2366             ret.card = ret.cards[last_card_n];
2367             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2368             ret.position = 'below';
2369         }
2370         
2371         if (this.items[ret.items_n].el == dragged_card_el) {
2372             return false;
2373         }
2374         
2375         if (ret.position == 'below') {
2376             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2377             
2378             if (card_after  && card_after.el == dragged_card_el) {
2379                 return false;
2380             }
2381             return ret;
2382         }
2383         
2384         // its's after ..
2385         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2386         
2387         if (card_before  && card_before.el == dragged_card_el) {
2388             return false;
2389         }
2390         
2391         return ret;
2392     },
2393     
2394     onNodeEnter : function(n, dd, e, data){
2395         return false;
2396     },
2397     onNodeOver : function(n, dd, e, data)
2398     {
2399        
2400         var target_info = this.getTargetFromEvent(e,data.source.el);
2401         if (target_info === false) {
2402             this.dropPlaceHolder('hide');
2403             return false;
2404         }
2405         Roo.log(['getTargetFromEvent', target_info ]);
2406         
2407          
2408         this.dropPlaceHolder('show', target_info,data);
2409         
2410         return false; 
2411     },
2412     onNodeOut : function(n, dd, e, data){
2413         this.dropPlaceHolder('hide');
2414      
2415     },
2416     onNodeDrop : function(n, dd, e, data)
2417     {
2418         
2419         // call drop - return false if
2420         
2421         // this could actually fail - if the Network drops..
2422         // we will ignore this at present..- client should probably reload
2423         // the whole set of cards if stuff like that fails.
2424         
2425         
2426         var info = this.getTargetFromEvent(e,data.source.el);
2427         if (info === false) {
2428             return false;
2429         }
2430         this.dropPlaceHolder('hide');
2431   
2432          
2433     
2434     
2435     
2436         this.acceptCard(data.source, info.position, info.card, info.items_n);
2437         return true;
2438          
2439     },
2440     firstChildCard : function()
2441     {
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             return this.items[i];
2448         }
2449         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2450     },
2451     /**
2452      * accept card
2453      *
2454      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2455
2456      */
2457     acceptCard : function(move_card,  position, next_to_card )
2458     {
2459         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2460             return false;
2461         }
2462         
2463         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2464         
2465         
2466         var dom = move_card.el.dom;
2467         dom.parentNode.removeChild(dom);
2468         
2469         
2470         if (next_to_card !== false) {
2471             var cardel = next_to_card.el.dom;
2472             
2473             if (position == 'above') {
2474                 cardel.parentNode.insertBefore(dom, cardel);
2475             } else if (cardel.nextSibling) {
2476                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2477             } else {
2478                 cardel.parentNode.append(dom);
2479             }
2480         } else {
2481             // card container???
2482             this.bodyEl.dom.append(dom);
2483         }
2484         
2485         //FIXME HANDLE card = true 
2486         
2487         // add this to the correct place in items.
2488         
2489         
2490         
2491         // remove Card from items.
2492         
2493         var old_parent = move_card.parent();
2494         
2495         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2496         
2497         if (this.items.length) {
2498             var nitems = [];
2499             //Roo.log([info.items_n, info.position, this.items.length]);
2500             for (var i =0; i < this.items.length; i++) {
2501                 if (i == to_items_n && position == 'above') {
2502                     nitems.push(move_card);
2503                 }
2504                 nitems.push(this.items[i]);
2505                 if (i == to_items_n && position == 'below') {
2506                     nitems.push(move_card);
2507                 }
2508             }
2509             this.items = nitems;
2510             Roo.log(this.items);
2511         } else {
2512             this.items.push(move_card);
2513         }
2514         
2515         move_card.parentId = this.id;
2516         
2517         return true;
2518         
2519         
2520     },
2521     
2522     
2523     /**    Decide whether to drop above or below a View node. */
2524     getDropPoint : function(e, n, dd)
2525     {
2526         if (dd) {
2527              return false;
2528         }
2529         if (n == this.bodyEl.dom) {
2530             return "above";
2531         }
2532         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2533         var c = t + (b - t) / 2;
2534         var y = Roo.lib.Event.getPageY(e);
2535         if(y <= c) {
2536             return "above";
2537         }else{
2538             return "below";
2539         }
2540     },
2541     onToggleCollapse : function(e)
2542         {
2543         if (this.collapsed) {
2544             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2545             this.collapsableEl.addClass('show');
2546             this.collapsed = false;
2547             return;
2548         }
2549         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2550         this.collapsableEl.removeClass('show');
2551         this.collapsed = true;
2552         
2553     
2554     },
2555     
2556     onToggleRotate : function(e)
2557     {
2558         this.collapsableEl.removeClass('show');
2559         this.footerEl.removeClass('d-none');
2560         this.el.removeClass('roo-card-rotated');
2561         this.el.removeClass('d-none');
2562         if (this.rotated) {
2563             
2564             this.collapsableEl.addClass('show');
2565             this.rotated = false;
2566             this.fireEvent('rotate', this, this.rotated);
2567             return;
2568         }
2569         this.el.addClass('roo-card-rotated');
2570         this.footerEl.addClass('d-none');
2571         this.el.select('.roo-collapsable').removeClass('show');
2572         
2573         this.rotated = true;
2574         this.fireEvent('rotate', this, this.rotated);
2575     
2576     },
2577     
2578     dropPlaceHolder: function (action, info, data)
2579     {
2580         if (this.dropEl === false) {
2581             this.dropEl = Roo.DomHelper.append(this.bodyEl, {
2582             cls : 'd-none'
2583             },true);
2584         }
2585         this.dropEl.removeClass(['d-none', 'd-block']);        
2586         if (action == 'hide') {
2587             
2588             this.dropEl.addClass('d-none');
2589             return;
2590         }
2591         // FIXME - info.card == true!!!
2592         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2593         
2594         if (info.card !== true) {
2595             var cardel = info.card.el.dom;
2596             
2597             if (info.position == 'above') {
2598                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2599             } else if (cardel.nextSibling) {
2600                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2601             } else {
2602                 cardel.parentNode.append(this.dropEl.dom);
2603             }
2604         } else {
2605             // card container???
2606             this.bodyEl.dom.append(this.dropEl.dom);
2607         }
2608         
2609         this.dropEl.addClass('d-block roo-card-dropzone');
2610         
2611         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2612         
2613         
2614     
2615     
2616     
2617     },
2618     setHeaderText: function(html)
2619     {
2620         this.headerEl.dom.innerHTML = html;
2621     }
2622
2623     
2624 });
2625
2626 /*
2627  * - LGPL
2628  *
2629  * Card header - holder for the card header elements.
2630  * 
2631  */
2632
2633 /**
2634  * @class Roo.bootstrap.CardHeader
2635  * @extends Roo.bootstrap.Element
2636  * Bootstrap CardHeader class
2637  * @constructor
2638  * Create a new Card Header - that you can embed children into
2639  * @param {Object} config The config object
2640  */
2641
2642 Roo.bootstrap.CardHeader = function(config){
2643     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2644 };
2645
2646 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2647     
2648     
2649     container_method : 'getCardHeader' 
2650     
2651      
2652     
2653     
2654    
2655 });
2656
2657  
2658
2659  /*
2660  * - LGPL
2661  *
2662  * Card footer - holder for the card footer elements.
2663  * 
2664  */
2665
2666 /**
2667  * @class Roo.bootstrap.CardFooter
2668  * @extends Roo.bootstrap.Element
2669  * Bootstrap CardFooter class
2670  * @constructor
2671  * Create a new Card Footer - that you can embed children into
2672  * @param {Object} config The config object
2673  */
2674
2675 Roo.bootstrap.CardFooter = function(config){
2676     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2677 };
2678
2679 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2680     
2681     
2682     container_method : 'getCardFooter' 
2683     
2684      
2685     
2686     
2687    
2688 });
2689
2690  
2691
2692  /*
2693  * - LGPL
2694  *
2695  * Card header - holder for the card header elements.
2696  * 
2697  */
2698
2699 /**
2700  * @class Roo.bootstrap.CardImageTop
2701  * @extends Roo.bootstrap.Element
2702  * Bootstrap CardImageTop class
2703  * @constructor
2704  * Create a new Card Image Top container
2705  * @param {Object} config The config object
2706  */
2707
2708 Roo.bootstrap.CardImageTop = function(config){
2709     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2710 };
2711
2712 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2713     
2714    
2715     container_method : 'getCardImageTop' 
2716     
2717      
2718     
2719    
2720 });
2721
2722  
2723
2724  /*
2725  * - LGPL
2726  *
2727  * image
2728  * 
2729  */
2730
2731
2732 /**
2733  * @class Roo.bootstrap.Img
2734  * @extends Roo.bootstrap.Component
2735  * Bootstrap Img class
2736  * @cfg {Boolean} imgResponsive false | true
2737  * @cfg {String} border rounded | circle | thumbnail
2738  * @cfg {String} src image source
2739  * @cfg {String} alt image alternative text
2740  * @cfg {String} href a tag href
2741  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2742  * @cfg {String} xsUrl xs image source
2743  * @cfg {String} smUrl sm image source
2744  * @cfg {String} mdUrl md image source
2745  * @cfg {String} lgUrl lg image source
2746  * 
2747  * @constructor
2748  * Create a new Input
2749  * @param {Object} config The config object
2750  */
2751
2752 Roo.bootstrap.Img = function(config){
2753     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2754     
2755     this.addEvents({
2756         // img events
2757         /**
2758          * @event click
2759          * The img click event for the img.
2760          * @param {Roo.EventObject} e
2761          */
2762         "click" : true
2763     });
2764 };
2765
2766 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2767     
2768     imgResponsive: true,
2769     border: '',
2770     src: 'about:blank',
2771     href: false,
2772     target: false,
2773     xsUrl: '',
2774     smUrl: '',
2775     mdUrl: '',
2776     lgUrl: '',
2777
2778     getAutoCreate : function()
2779     {   
2780         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2781             return this.createSingleImg();
2782         }
2783         
2784         var cfg = {
2785             tag: 'div',
2786             cls: 'roo-image-responsive-group',
2787             cn: []
2788         };
2789         var _this = this;
2790         
2791         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2792             
2793             if(!_this[size + 'Url']){
2794                 return;
2795             }
2796             
2797             var img = {
2798                 tag: 'img',
2799                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2800                 html: _this.html || cfg.html,
2801                 src: _this[size + 'Url']
2802             };
2803             
2804             img.cls += ' roo-image-responsive-' + size;
2805             
2806             var s = ['xs', 'sm', 'md', 'lg'];
2807             
2808             s.splice(s.indexOf(size), 1);
2809             
2810             Roo.each(s, function(ss){
2811                 img.cls += ' hidden-' + ss;
2812             });
2813             
2814             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2815                 cfg.cls += ' img-' + _this.border;
2816             }
2817             
2818             if(_this.alt){
2819                 cfg.alt = _this.alt;
2820             }
2821             
2822             if(_this.href){
2823                 var a = {
2824                     tag: 'a',
2825                     href: _this.href,
2826                     cn: [
2827                         img
2828                     ]
2829                 };
2830
2831                 if(this.target){
2832                     a.target = _this.target;
2833                 }
2834             }
2835             
2836             cfg.cn.push((_this.href) ? a : img);
2837             
2838         });
2839         
2840         return cfg;
2841     },
2842     
2843     createSingleImg : function()
2844     {
2845         var cfg = {
2846             tag: 'img',
2847             cls: (this.imgResponsive) ? 'img-responsive' : '',
2848             html : null,
2849             src : 'about:blank'  // just incase src get's set to undefined?!?
2850         };
2851         
2852         cfg.html = this.html || cfg.html;
2853         
2854         cfg.src = this.src || cfg.src;
2855         
2856         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2857             cfg.cls += ' img-' + this.border;
2858         }
2859         
2860         if(this.alt){
2861             cfg.alt = this.alt;
2862         }
2863         
2864         if(this.href){
2865             var a = {
2866                 tag: 'a',
2867                 href: this.href,
2868                 cn: [
2869                     cfg
2870                 ]
2871             };
2872             
2873             if(this.target){
2874                 a.target = this.target;
2875             }
2876             
2877         }
2878         
2879         return (this.href) ? a : cfg;
2880     },
2881     
2882     initEvents: function() 
2883     {
2884         if(!this.href){
2885             this.el.on('click', this.onClick, this);
2886         }
2887         
2888     },
2889     
2890     onClick : function(e)
2891     {
2892         Roo.log('img onclick');
2893         this.fireEvent('click', this, e);
2894     },
2895     /**
2896      * Sets the url of the image - used to update it
2897      * @param {String} url the url of the image
2898      */
2899     
2900     setSrc : function(url)
2901     {
2902         this.src =  url;
2903         
2904         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2905             this.el.dom.src =  url;
2906             return;
2907         }
2908         
2909         this.el.select('img', true).first().dom.src =  url;
2910     }
2911     
2912     
2913    
2914 });
2915
2916  /*
2917  * - LGPL
2918  *
2919  * image
2920  * 
2921  */
2922
2923
2924 /**
2925  * @class Roo.bootstrap.Link
2926  * @extends Roo.bootstrap.Component
2927  * Bootstrap Link Class
2928  * @cfg {String} alt image alternative text
2929  * @cfg {String} href a tag href
2930  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2931  * @cfg {String} html the content of the link.
2932  * @cfg {String} anchor name for the anchor link
2933  * @cfg {String} fa - favicon
2934
2935  * @cfg {Boolean} preventDefault (true | false) default false
2936
2937  * 
2938  * @constructor
2939  * Create a new Input
2940  * @param {Object} config The config object
2941  */
2942
2943 Roo.bootstrap.Link = function(config){
2944     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2945     
2946     this.addEvents({
2947         // img events
2948         /**
2949          * @event click
2950          * The img click event for the img.
2951          * @param {Roo.EventObject} e
2952          */
2953         "click" : true
2954     });
2955 };
2956
2957 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2958     
2959     href: false,
2960     target: false,
2961     preventDefault: false,
2962     anchor : false,
2963     alt : false,
2964     fa: false,
2965
2966
2967     getAutoCreate : function()
2968     {
2969         var html = this.html || '';
2970         
2971         if (this.fa !== false) {
2972             html = '<i class="fa fa-' + this.fa + '"></i>';
2973         }
2974         var cfg = {
2975             tag: 'a'
2976         };
2977         // anchor's do not require html/href...
2978         if (this.anchor === false) {
2979             cfg.html = html;
2980             cfg.href = this.href || '#';
2981         } else {
2982             cfg.name = this.anchor;
2983             if (this.html !== false || this.fa !== false) {
2984                 cfg.html = html;
2985             }
2986             if (this.href !== false) {
2987                 cfg.href = this.href;
2988             }
2989         }
2990         
2991         if(this.alt !== false){
2992             cfg.alt = this.alt;
2993         }
2994         
2995         
2996         if(this.target !== false) {
2997             cfg.target = this.target;
2998         }
2999         
3000         return cfg;
3001     },
3002     
3003     initEvents: function() {
3004         
3005         if(!this.href || this.preventDefault){
3006             this.el.on('click', this.onClick, this);
3007         }
3008     },
3009     
3010     onClick : function(e)
3011     {
3012         if(this.preventDefault){
3013             e.preventDefault();
3014         }
3015         //Roo.log('img onclick');
3016         this.fireEvent('click', this, e);
3017     }
3018    
3019 });
3020
3021  /*
3022  * - LGPL
3023  *
3024  * header
3025  * 
3026  */
3027
3028 /**
3029  * @class Roo.bootstrap.Header
3030  * @extends Roo.bootstrap.Component
3031  * Bootstrap Header class
3032  * @cfg {String} html content of header
3033  * @cfg {Number} level (1|2|3|4|5|6) default 1
3034  * 
3035  * @constructor
3036  * Create a new Header
3037  * @param {Object} config The config object
3038  */
3039
3040
3041 Roo.bootstrap.Header  = function(config){
3042     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3043 };
3044
3045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3046     
3047     //href : false,
3048     html : false,
3049     level : 1,
3050     
3051     
3052     
3053     getAutoCreate : function(){
3054         
3055         
3056         
3057         var cfg = {
3058             tag: 'h' + (1 *this.level),
3059             html: this.html || ''
3060         } ;
3061         
3062         return cfg;
3063     }
3064    
3065 });
3066
3067  
3068
3069  /*
3070  * Based on:
3071  * Ext JS Library 1.1.1
3072  * Copyright(c) 2006-2007, Ext JS, LLC.
3073  *
3074  * Originally Released Under LGPL - original licence link has changed is not relivant.
3075  *
3076  * Fork - LGPL
3077  * <script type="text/javascript">
3078  */
3079  
3080 /**
3081  * @class Roo.bootstrap.MenuMgr
3082  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3083  * @singleton
3084  */
3085 Roo.bootstrap.MenuMgr = function(){
3086    var menus, active, groups = {}, attached = false, lastShow = new Date();
3087
3088    // private - called when first menu is created
3089    function init(){
3090        menus = {};
3091        active = new Roo.util.MixedCollection();
3092        Roo.get(document).addKeyListener(27, function(){
3093            if(active.length > 0){
3094                hideAll();
3095            }
3096        });
3097    }
3098
3099    // private
3100    function hideAll(){
3101        if(active && active.length > 0){
3102            var c = active.clone();
3103            c.each(function(m){
3104                m.hide();
3105            });
3106        }
3107    }
3108
3109    // private
3110    function onHide(m){
3111        active.remove(m);
3112        if(active.length < 1){
3113            Roo.get(document).un("mouseup", onMouseDown);
3114             
3115            attached = false;
3116        }
3117    }
3118
3119    // private
3120    function onShow(m){
3121        var last = active.last();
3122        lastShow = new Date();
3123        active.add(m);
3124        if(!attached){
3125           Roo.get(document).on("mouseup", onMouseDown);
3126            
3127            attached = true;
3128        }
3129        if(m.parentMenu){
3130           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3131           m.parentMenu.activeChild = m;
3132        }else if(last && last.isVisible()){
3133           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3134        }
3135    }
3136
3137    // private
3138    function onBeforeHide(m){
3139        if(m.activeChild){
3140            m.activeChild.hide();
3141        }
3142        if(m.autoHideTimer){
3143            clearTimeout(m.autoHideTimer);
3144            delete m.autoHideTimer;
3145        }
3146    }
3147
3148    // private
3149    function onBeforeShow(m){
3150        var pm = m.parentMenu;
3151        if(!pm && !m.allowOtherMenus){
3152            hideAll();
3153        }else if(pm && pm.activeChild && active != m){
3154            pm.activeChild.hide();
3155        }
3156    }
3157
3158    // private this should really trigger on mouseup..
3159    function onMouseDown(e){
3160         Roo.log("on Mouse Up");
3161         
3162         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3163             Roo.log("MenuManager hideAll");
3164             hideAll();
3165             e.stopEvent();
3166         }
3167         
3168         
3169    }
3170
3171    // private
3172    function onBeforeCheck(mi, state){
3173        if(state){
3174            var g = groups[mi.group];
3175            for(var i = 0, l = g.length; i < l; i++){
3176                if(g[i] != mi){
3177                    g[i].setChecked(false);
3178                }
3179            }
3180        }
3181    }
3182
3183    return {
3184
3185        /**
3186         * Hides all menus that are currently visible
3187         */
3188        hideAll : function(){
3189             hideAll();  
3190        },
3191
3192        // private
3193        register : function(menu){
3194            if(!menus){
3195                init();
3196            }
3197            menus[menu.id] = menu;
3198            menu.on("beforehide", onBeforeHide);
3199            menu.on("hide", onHide);
3200            menu.on("beforeshow", onBeforeShow);
3201            menu.on("show", onShow);
3202            var g = menu.group;
3203            if(g && menu.events["checkchange"]){
3204                if(!groups[g]){
3205                    groups[g] = [];
3206                }
3207                groups[g].push(menu);
3208                menu.on("checkchange", onCheck);
3209            }
3210        },
3211
3212         /**
3213          * Returns a {@link Roo.menu.Menu} object
3214          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3215          * be used to generate and return a new Menu instance.
3216          */
3217        get : function(menu){
3218            if(typeof menu == "string"){ // menu id
3219                return menus[menu];
3220            }else if(menu.events){  // menu instance
3221                return menu;
3222            }
3223            /*else if(typeof menu.length == 'number'){ // array of menu items?
3224                return new Roo.bootstrap.Menu({items:menu});
3225            }else{ // otherwise, must be a config
3226                return new Roo.bootstrap.Menu(menu);
3227            }
3228            */
3229            return false;
3230        },
3231
3232        // private
3233        unregister : function(menu){
3234            delete menus[menu.id];
3235            menu.un("beforehide", onBeforeHide);
3236            menu.un("hide", onHide);
3237            menu.un("beforeshow", onBeforeShow);
3238            menu.un("show", onShow);
3239            var g = menu.group;
3240            if(g && menu.events["checkchange"]){
3241                groups[g].remove(menu);
3242                menu.un("checkchange", onCheck);
3243            }
3244        },
3245
3246        // private
3247        registerCheckable : function(menuItem){
3248            var g = menuItem.group;
3249            if(g){
3250                if(!groups[g]){
3251                    groups[g] = [];
3252                }
3253                groups[g].push(menuItem);
3254                menuItem.on("beforecheckchange", onBeforeCheck);
3255            }
3256        },
3257
3258        // private
3259        unregisterCheckable : function(menuItem){
3260            var g = menuItem.group;
3261            if(g){
3262                groups[g].remove(menuItem);
3263                menuItem.un("beforecheckchange", onBeforeCheck);
3264            }
3265        }
3266    };
3267 }();/*
3268  * - LGPL
3269  *
3270  * menu
3271  * 
3272  */
3273
3274 /**
3275  * @class Roo.bootstrap.Menu
3276  * @extends Roo.bootstrap.Component
3277  * Bootstrap Menu class - container for MenuItems
3278  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3279  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3280  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3281  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3282  * 
3283  * @constructor
3284  * Create a new Menu
3285  * @param {Object} config The config object
3286  */
3287
3288
3289 Roo.bootstrap.Menu = function(config){
3290     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3291     if (this.registerMenu && this.type != 'treeview')  {
3292         Roo.bootstrap.MenuMgr.register(this);
3293     }
3294     
3295     
3296     this.addEvents({
3297         /**
3298          * @event beforeshow
3299          * Fires before this menu is displayed (return false to block)
3300          * @param {Roo.menu.Menu} this
3301          */
3302         beforeshow : true,
3303         /**
3304          * @event beforehide
3305          * Fires before this menu is hidden (return false to block)
3306          * @param {Roo.menu.Menu} this
3307          */
3308         beforehide : true,
3309         /**
3310          * @event show
3311          * Fires after this menu is displayed
3312          * @param {Roo.menu.Menu} this
3313          */
3314         show : true,
3315         /**
3316          * @event hide
3317          * Fires after this menu is hidden
3318          * @param {Roo.menu.Menu} this
3319          */
3320         hide : true,
3321         /**
3322          * @event click
3323          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3324          * @param {Roo.menu.Menu} this
3325          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3326          * @param {Roo.EventObject} e
3327          */
3328         click : true,
3329         /**
3330          * @event mouseover
3331          * Fires when the mouse is hovering over this menu
3332          * @param {Roo.menu.Menu} this
3333          * @param {Roo.EventObject} e
3334          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3335          */
3336         mouseover : true,
3337         /**
3338          * @event mouseout
3339          * Fires when the mouse exits this menu
3340          * @param {Roo.menu.Menu} this
3341          * @param {Roo.EventObject} e
3342          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3343          */
3344         mouseout : true,
3345         /**
3346          * @event itemclick
3347          * Fires when a menu item contained in this menu is clicked
3348          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3349          * @param {Roo.EventObject} e
3350          */
3351         itemclick: true
3352     });
3353     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3354 };
3355
3356 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3357     
3358    /// html : false,
3359     //align : '',
3360     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3361     type: false,
3362     /**
3363      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3364      */
3365     registerMenu : true,
3366     
3367     menuItems :false, // stores the menu items..
3368     
3369     hidden:true,
3370         
3371     parentMenu : false,
3372     
3373     stopEvent : true,
3374     
3375     isLink : false,
3376     
3377     getChildContainer : function() {
3378         return this.el;  
3379     },
3380     
3381     getAutoCreate : function(){
3382          
3383         //if (['right'].indexOf(this.align)!==-1) {
3384         //    cfg.cn[1].cls += ' pull-right'
3385         //}
3386         
3387         
3388         var cfg = {
3389             tag : 'ul',
3390             cls : 'dropdown-menu' ,
3391             style : 'z-index:1000'
3392             
3393         };
3394         
3395         if (this.type === 'submenu') {
3396             cfg.cls = 'submenu active';
3397         }
3398         if (this.type === 'treeview') {
3399             cfg.cls = 'treeview-menu';
3400         }
3401         
3402         return cfg;
3403     },
3404     initEvents : function() {
3405         
3406        // Roo.log("ADD event");
3407        // Roo.log(this.triggerEl.dom);
3408         
3409         this.triggerEl.on('click', this.onTriggerClick, this);
3410         
3411         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3412         
3413         
3414         if (this.triggerEl.hasClass('nav-item')) {
3415             // dropdown toggle on the 'a' in BS4?
3416             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3417         } else {
3418             this.triggerEl.addClass('dropdown-toggle');
3419         }
3420         if (Roo.isTouch) {
3421             this.el.on('touchstart'  , this.onTouch, this);
3422         }
3423         this.el.on('click' , this.onClick, this);
3424
3425         this.el.on("mouseover", this.onMouseOver, this);
3426         this.el.on("mouseout", this.onMouseOut, this);
3427         
3428     },
3429     
3430     findTargetItem : function(e)
3431     {
3432         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3433         if(!t){
3434             return false;
3435         }
3436         //Roo.log(t);         Roo.log(t.id);
3437         if(t && t.id){
3438             //Roo.log(this.menuitems);
3439             return this.menuitems.get(t.id);
3440             
3441             //return this.items.get(t.menuItemId);
3442         }
3443         
3444         return false;
3445     },
3446     
3447     onTouch : function(e) 
3448     {
3449         Roo.log("menu.onTouch");
3450         //e.stopEvent(); this make the user popdown broken
3451         this.onClick(e);
3452     },
3453     
3454     onClick : function(e)
3455     {
3456         Roo.log("menu.onClick");
3457         
3458         var t = this.findTargetItem(e);
3459         if(!t || t.isContainer){
3460             return;
3461         }
3462         Roo.log(e);
3463         /*
3464         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3465             if(t == this.activeItem && t.shouldDeactivate(e)){
3466                 this.activeItem.deactivate();
3467                 delete this.activeItem;
3468                 return;
3469             }
3470             if(t.canActivate){
3471                 this.setActiveItem(t, true);
3472             }
3473             return;
3474             
3475             
3476         }
3477         */
3478        
3479         Roo.log('pass click event');
3480         
3481         t.onClick(e);
3482         
3483         this.fireEvent("click", this, t, e);
3484         
3485         var _this = this;
3486         
3487         if(!t.href.length || t.href == '#'){
3488             (function() { _this.hide(); }).defer(100);
3489         }
3490         
3491     },
3492     
3493     onMouseOver : function(e){
3494         var t  = this.findTargetItem(e);
3495         //Roo.log(t);
3496         //if(t){
3497         //    if(t.canActivate && !t.disabled){
3498         //        this.setActiveItem(t, true);
3499         //    }
3500         //}
3501         
3502         this.fireEvent("mouseover", this, e, t);
3503     },
3504     isVisible : function(){
3505         return !this.hidden;
3506     },
3507     onMouseOut : function(e){
3508         var t  = this.findTargetItem(e);
3509         
3510         //if(t ){
3511         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3512         //        this.activeItem.deactivate();
3513         //        delete this.activeItem;
3514         //    }
3515         //}
3516         this.fireEvent("mouseout", this, e, t);
3517     },
3518     
3519     
3520     /**
3521      * Displays this menu relative to another element
3522      * @param {String/HTMLElement/Roo.Element} element The element to align to
3523      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3524      * the element (defaults to this.defaultAlign)
3525      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3526      */
3527     show : function(el, pos, parentMenu)
3528     {
3529         if (false === this.fireEvent("beforeshow", this)) {
3530             Roo.log("show canceled");
3531             return;
3532         }
3533         this.parentMenu = parentMenu;
3534         if(!this.el){
3535             this.render();
3536         }
3537         
3538         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3539     },
3540      /**
3541      * Displays this menu at a specific xy position
3542      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3543      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3544      */
3545     showAt : function(xy, parentMenu, /* private: */_e){
3546         this.parentMenu = parentMenu;
3547         if(!this.el){
3548             this.render();
3549         }
3550         if(_e !== false){
3551             this.fireEvent("beforeshow", this);
3552             //xy = this.el.adjustForConstraints(xy);
3553         }
3554         
3555         //this.el.show();
3556         this.hideMenuItems();
3557         this.hidden = false;
3558         this.triggerEl.addClass('open');
3559         this.el.addClass('show');
3560         
3561         // reassign x when hitting right
3562         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3563             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3564         }
3565         
3566         // reassign y when hitting bottom
3567         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3568             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3569         }
3570         
3571         // but the list may align on trigger left or trigger top... should it be a properity?
3572         
3573         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3574             this.el.setXY(xy);
3575         }
3576         
3577         this.focus();
3578         this.fireEvent("show", this);
3579     },
3580     
3581     focus : function(){
3582         return;
3583         if(!this.hidden){
3584             this.doFocus.defer(50, this);
3585         }
3586     },
3587
3588     doFocus : function(){
3589         if(!this.hidden){
3590             this.focusEl.focus();
3591         }
3592     },
3593
3594     /**
3595      * Hides this menu and optionally all parent menus
3596      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3597      */
3598     hide : function(deep)
3599     {
3600         if (false === this.fireEvent("beforehide", this)) {
3601             Roo.log("hide canceled");
3602             return;
3603         }
3604         this.hideMenuItems();
3605         if(this.el && this.isVisible()){
3606            
3607             if(this.activeItem){
3608                 this.activeItem.deactivate();
3609                 this.activeItem = null;
3610             }
3611             this.triggerEl.removeClass('open');;
3612             this.el.removeClass('show');
3613             this.hidden = true;
3614             this.fireEvent("hide", this);
3615         }
3616         if(deep === true && this.parentMenu){
3617             this.parentMenu.hide(true);
3618         }
3619     },
3620     
3621     onTriggerClick : function(e)
3622     {
3623         Roo.log('trigger click');
3624         
3625         var target = e.getTarget();
3626         
3627         Roo.log(target.nodeName.toLowerCase());
3628         
3629         if(target.nodeName.toLowerCase() === 'i'){
3630             e.preventDefault();
3631         }
3632         
3633     },
3634     
3635     onTriggerPress  : function(e)
3636     {
3637         Roo.log('trigger press');
3638         //Roo.log(e.getTarget());
3639        // Roo.log(this.triggerEl.dom);
3640        
3641         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3642         var pel = Roo.get(e.getTarget());
3643         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3644             Roo.log('is treeview or dropdown?');
3645             return;
3646         }
3647         
3648         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3649             return;
3650         }
3651         
3652         if (this.isVisible()) {
3653             Roo.log('hide');
3654             this.hide();
3655         } else {
3656             Roo.log('show');
3657             this.show(this.triggerEl, '?', false);
3658         }
3659         
3660         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3661             e.stopEvent();
3662         }
3663         
3664     },
3665        
3666     
3667     hideMenuItems : function()
3668     {
3669         Roo.log("hide Menu Items");
3670         if (!this.el) { 
3671             return;
3672         }
3673         
3674         this.el.select('.open',true).each(function(aa) {
3675             
3676             aa.removeClass('open');
3677          
3678         });
3679     },
3680     addxtypeChild : function (tree, cntr) {
3681         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3682           
3683         this.menuitems.add(comp);
3684         return comp;
3685
3686     },
3687     getEl : function()
3688     {
3689         Roo.log(this.el);
3690         return this.el;
3691     },
3692     
3693     clear : function()
3694     {
3695         this.getEl().dom.innerHTML = '';
3696         this.menuitems.clear();
3697     }
3698 });
3699
3700  
3701  /*
3702  * - LGPL
3703  *
3704  * menu item
3705  * 
3706  */
3707
3708
3709 /**
3710  * @class Roo.bootstrap.MenuItem
3711  * @extends Roo.bootstrap.Component
3712  * Bootstrap MenuItem class
3713  * @cfg {String} html the menu label
3714  * @cfg {String} href the link
3715  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3716  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3717  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3718  * @cfg {String} fa favicon to show on left of menu item.
3719  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3720  * 
3721  * 
3722  * @constructor
3723  * Create a new MenuItem
3724  * @param {Object} config The config object
3725  */
3726
3727
3728 Roo.bootstrap.MenuItem = function(config){
3729     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3730     this.addEvents({
3731         // raw events
3732         /**
3733          * @event click
3734          * The raw click event for the entire grid.
3735          * @param {Roo.bootstrap.MenuItem} this
3736          * @param {Roo.EventObject} e
3737          */
3738         "click" : true
3739     });
3740 };
3741
3742 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3743     
3744     href : false,
3745     html : false,
3746     preventDefault: false,
3747     isContainer : false,
3748     active : false,
3749     fa: false,
3750     
3751     getAutoCreate : function(){
3752         
3753         if(this.isContainer){
3754             return {
3755                 tag: 'li',
3756                 cls: 'dropdown-menu-item '
3757             };
3758         }
3759         var ctag = {
3760             tag: 'span',
3761             html: 'Link'
3762         };
3763         
3764         var anc = {
3765             tag : 'a',
3766             cls : 'dropdown-item',
3767             href : '#',
3768             cn : [  ]
3769         };
3770         
3771         if (this.fa !== false) {
3772             anc.cn.push({
3773                 tag : 'i',
3774                 cls : 'fa fa-' + this.fa
3775             });
3776         }
3777         
3778         anc.cn.push(ctag);
3779         
3780         
3781         var cfg= {
3782             tag: 'li',
3783             cls: 'dropdown-menu-item',
3784             cn: [ anc ]
3785         };
3786         if (this.parent().type == 'treeview') {
3787             cfg.cls = 'treeview-menu';
3788         }
3789         if (this.active) {
3790             cfg.cls += ' active';
3791         }
3792         
3793         
3794         
3795         anc.href = this.href || cfg.cn[0].href ;
3796         ctag.html = this.html || cfg.cn[0].html ;
3797         return cfg;
3798     },
3799     
3800     initEvents: function()
3801     {
3802         if (this.parent().type == 'treeview') {
3803             this.el.select('a').on('click', this.onClick, this);
3804         }
3805         
3806         if (this.menu) {
3807             this.menu.parentType = this.xtype;
3808             this.menu.triggerEl = this.el;
3809             this.menu = this.addxtype(Roo.apply({}, this.menu));
3810         }
3811         
3812     },
3813     onClick : function(e)
3814     {
3815         Roo.log('item on click ');
3816         
3817         if(this.preventDefault){
3818             e.preventDefault();
3819         }
3820         //this.parent().hideMenuItems();
3821         
3822         this.fireEvent('click', this, e);
3823     },
3824     getEl : function()
3825     {
3826         return this.el;
3827     } 
3828 });
3829
3830  
3831
3832  /*
3833  * - LGPL
3834  *
3835  * menu separator
3836  * 
3837  */
3838
3839
3840 /**
3841  * @class Roo.bootstrap.MenuSeparator
3842  * @extends Roo.bootstrap.Component
3843  * Bootstrap MenuSeparator class
3844  * 
3845  * @constructor
3846  * Create a new MenuItem
3847  * @param {Object} config The config object
3848  */
3849
3850
3851 Roo.bootstrap.MenuSeparator = function(config){
3852     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3853 };
3854
3855 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3856     
3857     getAutoCreate : function(){
3858         var cfg = {
3859             cls: 'divider',
3860             tag : 'li'
3861         };
3862         
3863         return cfg;
3864     }
3865    
3866 });
3867
3868  
3869
3870  
3871 /*
3872 * Licence: LGPL
3873 */
3874
3875 /**
3876  * @class Roo.bootstrap.Modal
3877  * @extends Roo.bootstrap.Component
3878  * Bootstrap Modal class
3879  * @cfg {String} title Title of dialog
3880  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3881  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3882  * @cfg {Boolean} specificTitle default false
3883  * @cfg {Array} buttons Array of buttons or standard button set..
3884  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3885  * @cfg {Boolean} animate default true
3886  * @cfg {Boolean} allow_close default true
3887  * @cfg {Boolean} fitwindow default false
3888  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3889  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3890  * @cfg {String} size (sm|lg|xl) default empty
3891  * @cfg {Number} max_width set the max width of modal
3892  * @cfg {Boolean} editableTitle can the title be edited
3893
3894  *
3895  *
3896  * @constructor
3897  * Create a new Modal Dialog
3898  * @param {Object} config The config object
3899  */
3900
3901 Roo.bootstrap.Modal = function(config){
3902     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3903     this.addEvents({
3904         // raw events
3905         /**
3906          * @event btnclick
3907          * The raw btnclick event for the button
3908          * @param {Roo.EventObject} e
3909          */
3910         "btnclick" : true,
3911         /**
3912          * @event resize
3913          * Fire when dialog resize
3914          * @param {Roo.bootstrap.Modal} this
3915          * @param {Roo.EventObject} e
3916          */
3917         "resize" : true,
3918         /**
3919          * @event titlechanged
3920          * Fire when the editable title has been changed
3921          * @param {Roo.bootstrap.Modal} this
3922          * @param {Roo.EventObject} value
3923          */
3924         "titlechanged" : true 
3925         
3926     });
3927     this.buttons = this.buttons || [];
3928
3929     if (this.tmpl) {
3930         this.tmpl = Roo.factory(this.tmpl);
3931     }
3932
3933 };
3934
3935 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3936
3937     title : 'test dialog',
3938
3939     buttons : false,
3940
3941     // set on load...
3942
3943     html: false,
3944
3945     tmp: false,
3946
3947     specificTitle: false,
3948
3949     buttonPosition: 'right',
3950
3951     allow_close : true,
3952
3953     animate : true,
3954
3955     fitwindow: false,
3956     
3957      // private
3958     dialogEl: false,
3959     bodyEl:  false,
3960     footerEl:  false,
3961     titleEl:  false,
3962     closeEl:  false,
3963
3964     size: '',
3965     
3966     max_width: 0,
3967     
3968     max_height: 0,
3969     
3970     fit_content: false,
3971     editableTitle  : false,
3972
3973     onRender : function(ct, position)
3974     {
3975         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3976
3977         if(!this.el){
3978             var cfg = Roo.apply({},  this.getAutoCreate());
3979             cfg.id = Roo.id();
3980             //if(!cfg.name){
3981             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3982             //}
3983             //if (!cfg.name.length) {
3984             //    delete cfg.name;
3985            // }
3986             if (this.cls) {
3987                 cfg.cls += ' ' + this.cls;
3988             }
3989             if (this.style) {
3990                 cfg.style = this.style;
3991             }
3992             this.el = Roo.get(document.body).createChild(cfg, position);
3993         }
3994         //var type = this.el.dom.type;
3995
3996
3997         if(this.tabIndex !== undefined){
3998             this.el.dom.setAttribute('tabIndex', this.tabIndex);
3999         }
4000
4001         this.dialogEl = this.el.select('.modal-dialog',true).first();
4002         this.bodyEl = this.el.select('.modal-body',true).first();
4003         this.closeEl = this.el.select('.modal-header .close', true).first();
4004         this.headerEl = this.el.select('.modal-header',true).first();
4005         this.titleEl = this.el.select('.modal-title',true).first();
4006         this.footerEl = this.el.select('.modal-footer',true).first();
4007
4008         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4009         
4010         //this.el.addClass("x-dlg-modal");
4011
4012         if (this.buttons.length) {
4013             Roo.each(this.buttons, function(bb) {
4014                 var b = Roo.apply({}, bb);
4015                 b.xns = b.xns || Roo.bootstrap;
4016                 b.xtype = b.xtype || 'Button';
4017                 if (typeof(b.listeners) == 'undefined') {
4018                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4019                 }
4020
4021                 var btn = Roo.factory(b);
4022
4023                 btn.render(this.getButtonContainer());
4024
4025             },this);
4026         }
4027         // render the children.
4028         var nitems = [];
4029
4030         if(typeof(this.items) != 'undefined'){
4031             var items = this.items;
4032             delete this.items;
4033
4034             for(var i =0;i < items.length;i++) {
4035                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4036             }
4037         }
4038
4039         this.items = nitems;
4040
4041         // where are these used - they used to be body/close/footer
4042
4043
4044         this.initEvents();
4045         //this.el.addClass([this.fieldClass, this.cls]);
4046
4047     },
4048
4049     getAutoCreate : function()
4050     {
4051         // we will default to modal-body-overflow - might need to remove or make optional later.
4052         var bdy = {
4053                 cls : 'modal-body enable-modal-body-overflow ', 
4054                 html : this.html || ''
4055         };
4056
4057         var title = {
4058             tag: 'h4',
4059             cls : 'modal-title',
4060             html : this.title
4061         };
4062
4063         if(this.specificTitle){ // WTF is this?
4064             title = this.title;
4065         }
4066
4067         var header = [];
4068         if (this.allow_close && Roo.bootstrap.version == 3) {
4069             header.push({
4070                 tag: 'button',
4071                 cls : 'close',
4072                 html : '&times'
4073             });
4074         }
4075
4076         header.push(title);
4077
4078         if (this.editableTitle) {
4079             header.push({
4080                 cls: 'form-control roo-editable-title d-none',
4081                 tag: 'input',
4082                 type: 'text'
4083             });
4084         }
4085         
4086         if (this.allow_close && Roo.bootstrap.version == 4) {
4087             header.push({
4088                 tag: 'button',
4089                 cls : 'close',
4090                 html : '&times'
4091             });
4092         }
4093         
4094         var size = '';
4095
4096         if(this.size.length){
4097             size = 'modal-' + this.size;
4098         }
4099         
4100         var footer = Roo.bootstrap.version == 3 ?
4101             {
4102                 cls : 'modal-footer',
4103                 cn : [
4104                     {
4105                         tag: 'div',
4106                         cls: 'btn-' + this.buttonPosition
4107                     }
4108                 ]
4109
4110             } :
4111             {  // BS4 uses mr-auto on left buttons....
4112                 cls : 'modal-footer'
4113             };
4114
4115             
4116
4117         
4118         
4119         var modal = {
4120             cls: "modal",
4121              cn : [
4122                 {
4123                     cls: "modal-dialog " + size,
4124                     cn : [
4125                         {
4126                             cls : "modal-content",
4127                             cn : [
4128                                 {
4129                                     cls : 'modal-header',
4130                                     cn : header
4131                                 },
4132                                 bdy,
4133                                 footer
4134                             ]
4135
4136                         }
4137                     ]
4138
4139                 }
4140             ]
4141         };
4142
4143         if(this.animate){
4144             modal.cls += ' fade';
4145         }
4146
4147         return modal;
4148
4149     },
4150     getChildContainer : function() {
4151
4152          return this.bodyEl;
4153
4154     },
4155     getButtonContainer : function() {
4156         
4157          return Roo.bootstrap.version == 4 ?
4158             this.el.select('.modal-footer',true).first()
4159             : this.el.select('.modal-footer div',true).first();
4160
4161     },
4162     initEvents : function()
4163     {
4164         if (this.allow_close) {
4165             this.closeEl.on('click', this.hide, this);
4166         }
4167         Roo.EventManager.onWindowResize(this.resize, this, true);
4168         if (this.editableTitle) {
4169             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4170             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4171             this.headerEditEl.on('keyup', function(e) {
4172                     if(e.isNavKeyPress()){
4173                             this.toggleHeaderInput(false)
4174                     }
4175                 }, this);
4176             this.headerEditEl.on('blur', function(e) {
4177                 this.toggleHeaderInput(false)
4178             },this);
4179         }
4180
4181     },
4182   
4183
4184     resize : function()
4185     {
4186         this.maskEl.setSize(
4187             Roo.lib.Dom.getViewWidth(true),
4188             Roo.lib.Dom.getViewHeight(true)
4189         );
4190         
4191         if (this.fitwindow) {
4192             
4193            
4194             this.setSize(
4195                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4196                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4197             );
4198             return;
4199         }
4200         
4201         if(this.max_width !== 0) {
4202             
4203             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4204             
4205             if(this.height) {
4206                 this.setSize(w, this.height);
4207                 return;
4208             }
4209             
4210             if(this.max_height) {
4211                 this.setSize(w,Math.min(
4212                     this.max_height,
4213                     Roo.lib.Dom.getViewportHeight(true) - 60
4214                 ));
4215                 
4216                 return;
4217             }
4218             
4219             if(!this.fit_content) {
4220                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4221                 return;
4222             }
4223             
4224             this.setSize(w, Math.min(
4225                 60 +
4226                 this.headerEl.getHeight() + 
4227                 this.footerEl.getHeight() + 
4228                 this.getChildHeight(this.bodyEl.dom.childNodes),
4229                 Roo.lib.Dom.getViewportHeight(true) - 60)
4230             );
4231         }
4232         
4233     },
4234
4235     setSize : function(w,h)
4236     {
4237         if (!w && !h) {
4238             return;
4239         }
4240         
4241         this.resizeTo(w,h);
4242     },
4243
4244     show : function() {
4245
4246         if (!this.rendered) {
4247             this.render();
4248         }
4249
4250         //this.el.setStyle('display', 'block');
4251         this.el.removeClass('hideing');
4252         this.el.dom.style.display='block';
4253         
4254         Roo.get(document.body).addClass('modal-open');
4255  
4256         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4257             
4258             (function(){
4259                 this.el.addClass('show');
4260                 this.el.addClass('in');
4261             }).defer(50, this);
4262         }else{
4263             this.el.addClass('show');
4264             this.el.addClass('in');
4265         }
4266
4267         // not sure how we can show data in here..
4268         //if (this.tmpl) {
4269         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4270         //}
4271
4272         Roo.get(document.body).addClass("x-body-masked");
4273         
4274         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4275         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4276         this.maskEl.dom.style.display = 'block';
4277         this.maskEl.addClass('show');
4278         
4279         
4280         this.resize();
4281         
4282         this.fireEvent('show', this);
4283
4284         // set zindex here - otherwise it appears to be ignored...
4285         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4286
4287         (function () {
4288             this.items.forEach( function(e) {
4289                 e.layout ? e.layout() : false;
4290
4291             });
4292         }).defer(100,this);
4293
4294     },
4295     hide : function()
4296     {
4297         if(this.fireEvent("beforehide", this) !== false){
4298             
4299             this.maskEl.removeClass('show');
4300             
4301             this.maskEl.dom.style.display = '';
4302             Roo.get(document.body).removeClass("x-body-masked");
4303             this.el.removeClass('in');
4304             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4305
4306             if(this.animate){ // why
4307                 this.el.addClass('hideing');
4308                 this.el.removeClass('show');
4309                 (function(){
4310                     if (!this.el.hasClass('hideing')) {
4311                         return; // it's been shown again...
4312                     }
4313                     
4314                     this.el.dom.style.display='';
4315
4316                     Roo.get(document.body).removeClass('modal-open');
4317                     this.el.removeClass('hideing');
4318                 }).defer(150,this);
4319                 
4320             }else{
4321                 this.el.removeClass('show');
4322                 this.el.dom.style.display='';
4323                 Roo.get(document.body).removeClass('modal-open');
4324
4325             }
4326             this.fireEvent('hide', this);
4327         }
4328     },
4329     isVisible : function()
4330     {
4331         
4332         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4333         
4334     },
4335
4336     addButton : function(str, cb)
4337     {
4338
4339
4340         var b = Roo.apply({}, { html : str } );
4341         b.xns = b.xns || Roo.bootstrap;
4342         b.xtype = b.xtype || 'Button';
4343         if (typeof(b.listeners) == 'undefined') {
4344             b.listeners = { click : cb.createDelegate(this)  };
4345         }
4346
4347         var btn = Roo.factory(b);
4348
4349         btn.render(this.getButtonContainer());
4350
4351         return btn;
4352
4353     },
4354
4355     setDefaultButton : function(btn)
4356     {
4357         //this.el.select('.modal-footer').()
4358     },
4359
4360     resizeTo: function(w,h)
4361     {
4362         this.dialogEl.setWidth(w);
4363         
4364         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4365
4366         this.bodyEl.setHeight(h - diff);
4367         
4368         this.fireEvent('resize', this);
4369     },
4370     
4371     setContentSize  : function(w, h)
4372     {
4373
4374     },
4375     onButtonClick: function(btn,e)
4376     {
4377         //Roo.log([a,b,c]);
4378         this.fireEvent('btnclick', btn.name, e);
4379     },
4380      /**
4381      * Set the title of the Dialog
4382      * @param {String} str new Title
4383      */
4384     setTitle: function(str) {
4385         this.titleEl.dom.innerHTML = str;
4386         this.title = str;
4387     },
4388     /**
4389      * Set the body of the Dialog
4390      * @param {String} str new Title
4391      */
4392     setBody: function(str) {
4393         this.bodyEl.dom.innerHTML = str;
4394     },
4395     /**
4396      * Set the body of the Dialog using the template
4397      * @param {Obj} data - apply this data to the template and replace the body contents.
4398      */
4399     applyBody: function(obj)
4400     {
4401         if (!this.tmpl) {
4402             Roo.log("Error - using apply Body without a template");
4403             //code
4404         }
4405         this.tmpl.overwrite(this.bodyEl, obj);
4406     },
4407     
4408     getChildHeight : function(child_nodes)
4409     {
4410         if(
4411             !child_nodes ||
4412             child_nodes.length == 0
4413         ) {
4414             return 0;
4415         }
4416         
4417         var child_height = 0;
4418         
4419         for(var i = 0; i < child_nodes.length; i++) {
4420             
4421             /*
4422             * for modal with tabs...
4423             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4424                 
4425                 var layout_childs = child_nodes[i].childNodes;
4426                 
4427                 for(var j = 0; j < layout_childs.length; j++) {
4428                     
4429                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4430                         
4431                         var layout_body_childs = layout_childs[j].childNodes;
4432                         
4433                         for(var k = 0; k < layout_body_childs.length; k++) {
4434                             
4435                             if(layout_body_childs[k].classList.contains('navbar')) {
4436                                 child_height += layout_body_childs[k].offsetHeight;
4437                                 continue;
4438                             }
4439                             
4440                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4441                                 
4442                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4443                                 
4444                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4445                                     
4446                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4447                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4448                                         continue;
4449                                     }
4450                                     
4451                                 }
4452                                 
4453                             }
4454                             
4455                         }
4456                     }
4457                 }
4458                 continue;
4459             }
4460             */
4461             
4462             child_height += child_nodes[i].offsetHeight;
4463             // Roo.log(child_nodes[i].offsetHeight);
4464         }
4465         
4466         return child_height;
4467     },
4468     toggleHeaderInput : function(is_edit)
4469     {
4470         
4471         if (is_edit && this.is_header_editing) {
4472             return; // already editing..
4473         }
4474         if (is_edit) {
4475     
4476             this.headerEditEl.dom.value = this.title;
4477             this.headerEditEl.removeClass('d-none');
4478             this.headerEditEl.dom.focus();
4479             this.titleEl.addClass('d-none');
4480             
4481             this.is_header_editing = true;
4482             return
4483         }
4484         // flip back to not editing.
4485         this.title = this.headerEditEl.dom.value;
4486         this.headerEditEl.addClass('d-none');
4487         this.titleEl.removeClass('d-none');
4488         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4489         this.is_header_editing = false;
4490         this.fireEvent('titlechanged', this, this.title);
4491     
4492             
4493         
4494     }
4495
4496 });
4497
4498
4499 Roo.apply(Roo.bootstrap.Modal,  {
4500     /**
4501          * Button config that displays a single OK button
4502          * @type Object
4503          */
4504         OK :  [{
4505             name : 'ok',
4506             weight : 'primary',
4507             html : 'OK'
4508         }],
4509         /**
4510          * Button config that displays Yes and No buttons
4511          * @type Object
4512          */
4513         YESNO : [
4514             {
4515                 name  : 'no',
4516                 html : 'No'
4517             },
4518             {
4519                 name  :'yes',
4520                 weight : 'primary',
4521                 html : 'Yes'
4522             }
4523         ],
4524
4525         /**
4526          * Button config that displays OK and Cancel buttons
4527          * @type Object
4528          */
4529         OKCANCEL : [
4530             {
4531                name : 'cancel',
4532                 html : 'Cancel'
4533             },
4534             {
4535                 name : 'ok',
4536                 weight : 'primary',
4537                 html : 'OK'
4538             }
4539         ],
4540         /**
4541          * Button config that displays Yes, No and Cancel buttons
4542          * @type Object
4543          */
4544         YESNOCANCEL : [
4545             {
4546                 name : 'yes',
4547                 weight : 'primary',
4548                 html : 'Yes'
4549             },
4550             {
4551                 name : 'no',
4552                 html : 'No'
4553             },
4554             {
4555                 name : 'cancel',
4556                 html : 'Cancel'
4557             }
4558         ],
4559         
4560         zIndex : 10001
4561 });
4562
4563 /*
4564  * - LGPL
4565  *
4566  * messagebox - can be used as a replace
4567  * 
4568  */
4569 /**
4570  * @class Roo.MessageBox
4571  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4572  * Example usage:
4573  *<pre><code>
4574 // Basic alert:
4575 Roo.Msg.alert('Status', 'Changes saved successfully.');
4576
4577 // Prompt for user data:
4578 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4579     if (btn == 'ok'){
4580         // process text value...
4581     }
4582 });
4583
4584 // Show a dialog using config options:
4585 Roo.Msg.show({
4586    title:'Save Changes?',
4587    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4588    buttons: Roo.Msg.YESNOCANCEL,
4589    fn: processResult,
4590    animEl: 'elId'
4591 });
4592 </code></pre>
4593  * @singleton
4594  */
4595 Roo.bootstrap.MessageBox = function(){
4596     var dlg, opt, mask, waitTimer;
4597     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4598     var buttons, activeTextEl, bwidth;
4599
4600     
4601     // private
4602     var handleButton = function(button){
4603         dlg.hide();
4604         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4605     };
4606
4607     // private
4608     var handleHide = function(){
4609         if(opt && opt.cls){
4610             dlg.el.removeClass(opt.cls);
4611         }
4612         //if(waitTimer){
4613         //    Roo.TaskMgr.stop(waitTimer);
4614         //    waitTimer = null;
4615         //}
4616     };
4617
4618     // private
4619     var updateButtons = function(b){
4620         var width = 0;
4621         if(!b){
4622             buttons["ok"].hide();
4623             buttons["cancel"].hide();
4624             buttons["yes"].hide();
4625             buttons["no"].hide();
4626             dlg.footerEl.hide();
4627             
4628             return width;
4629         }
4630         dlg.footerEl.show();
4631         for(var k in buttons){
4632             if(typeof buttons[k] != "function"){
4633                 if(b[k]){
4634                     buttons[k].show();
4635                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4636                     width += buttons[k].el.getWidth()+15;
4637                 }else{
4638                     buttons[k].hide();
4639                 }
4640             }
4641         }
4642         return width;
4643     };
4644
4645     // private
4646     var handleEsc = function(d, k, e){
4647         if(opt && opt.closable !== false){
4648             dlg.hide();
4649         }
4650         if(e){
4651             e.stopEvent();
4652         }
4653     };
4654
4655     return {
4656         /**
4657          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4658          * @return {Roo.BasicDialog} The BasicDialog element
4659          */
4660         getDialog : function(){
4661            if(!dlg){
4662                 dlg = new Roo.bootstrap.Modal( {
4663                     //draggable: true,
4664                     //resizable:false,
4665                     //constraintoviewport:false,
4666                     //fixedcenter:true,
4667                     //collapsible : false,
4668                     //shim:true,
4669                     //modal: true,
4670                 //    width: 'auto',
4671                   //  height:100,
4672                     //buttonAlign:"center",
4673                     closeClick : function(){
4674                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4675                             handleButton("no");
4676                         }else{
4677                             handleButton("cancel");
4678                         }
4679                     }
4680                 });
4681                 dlg.render();
4682                 dlg.on("hide", handleHide);
4683                 mask = dlg.mask;
4684                 //dlg.addKeyListener(27, handleEsc);
4685                 buttons = {};
4686                 this.buttons = buttons;
4687                 var bt = this.buttonText;
4688                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4689                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4690                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4691                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4692                 //Roo.log(buttons);
4693                 bodyEl = dlg.bodyEl.createChild({
4694
4695                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4696                         '<textarea class="roo-mb-textarea"></textarea>' +
4697                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4698                 });
4699                 msgEl = bodyEl.dom.firstChild;
4700                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4701                 textboxEl.enableDisplayMode();
4702                 textboxEl.addKeyListener([10,13], function(){
4703                     if(dlg.isVisible() && opt && opt.buttons){
4704                         if(opt.buttons.ok){
4705                             handleButton("ok");
4706                         }else if(opt.buttons.yes){
4707                             handleButton("yes");
4708                         }
4709                     }
4710                 });
4711                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4712                 textareaEl.enableDisplayMode();
4713                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4714                 progressEl.enableDisplayMode();
4715                 
4716                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4717                 var pf = progressEl.dom.firstChild;
4718                 if (pf) {
4719                     pp = Roo.get(pf.firstChild);
4720                     pp.setHeight(pf.offsetHeight);
4721                 }
4722                 
4723             }
4724             return dlg;
4725         },
4726
4727         /**
4728          * Updates the message box body text
4729          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4730          * the XHTML-compliant non-breaking space character '&amp;#160;')
4731          * @return {Roo.MessageBox} This message box
4732          */
4733         updateText : function(text)
4734         {
4735             if(!dlg.isVisible() && !opt.width){
4736                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4737                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4738             }
4739             msgEl.innerHTML = text || '&#160;';
4740       
4741             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4742             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4743             var w = Math.max(
4744                     Math.min(opt.width || cw , this.maxWidth), 
4745                     Math.max(opt.minWidth || this.minWidth, bwidth)
4746             );
4747             if(opt.prompt){
4748                 activeTextEl.setWidth(w);
4749             }
4750             if(dlg.isVisible()){
4751                 dlg.fixedcenter = false;
4752             }
4753             // to big, make it scroll. = But as usual stupid IE does not support
4754             // !important..
4755             
4756             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4757                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4758                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4759             } else {
4760                 bodyEl.dom.style.height = '';
4761                 bodyEl.dom.style.overflowY = '';
4762             }
4763             if (cw > w) {
4764                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4765             } else {
4766                 bodyEl.dom.style.overflowX = '';
4767             }
4768             
4769             dlg.setContentSize(w, bodyEl.getHeight());
4770             if(dlg.isVisible()){
4771                 dlg.fixedcenter = true;
4772             }
4773             return this;
4774         },
4775
4776         /**
4777          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4778          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4779          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4780          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4781          * @return {Roo.MessageBox} This message box
4782          */
4783         updateProgress : function(value, text){
4784             if(text){
4785                 this.updateText(text);
4786             }
4787             
4788             if (pp) { // weird bug on my firefox - for some reason this is not defined
4789                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4790                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4791             }
4792             return this;
4793         },        
4794
4795         /**
4796          * Returns true if the message box is currently displayed
4797          * @return {Boolean} True if the message box is visible, else false
4798          */
4799         isVisible : function(){
4800             return dlg && dlg.isVisible();  
4801         },
4802
4803         /**
4804          * Hides the message box if it is displayed
4805          */
4806         hide : function(){
4807             if(this.isVisible()){
4808                 dlg.hide();
4809             }  
4810         },
4811
4812         /**
4813          * Displays a new message box, or reinitializes an existing message box, based on the config options
4814          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4815          * The following config object properties are supported:
4816          * <pre>
4817 Property    Type             Description
4818 ----------  ---------------  ------------------------------------------------------------------------------------
4819 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4820                                    closes (defaults to undefined)
4821 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4822                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4823 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4824                                    progress and wait dialogs will ignore this property and always hide the
4825                                    close button as they can only be closed programmatically.
4826 cls               String           A custom CSS class to apply to the message box element
4827 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4828                                    displayed (defaults to 75)
4829 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4830                                    function will be btn (the name of the button that was clicked, if applicable,
4831                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4832                                    Progress and wait dialogs will ignore this option since they do not respond to
4833                                    user actions and can only be closed programmatically, so any required function
4834                                    should be called by the same code after it closes the dialog.
4835 icon              String           A CSS class that provides a background image to be used as an icon for
4836                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4837 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4838 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4839 modal             Boolean          False to allow user interaction with the page while the message box is
4840                                    displayed (defaults to true)
4841 msg               String           A string that will replace the existing message box body text (defaults
4842                                    to the XHTML-compliant non-breaking space character '&#160;')
4843 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4844 progress          Boolean          True to display a progress bar (defaults to false)
4845 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4846 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4847 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4848 title             String           The title text
4849 value             String           The string value to set into the active textbox element if displayed
4850 wait              Boolean          True to display a progress bar (defaults to false)
4851 width             Number           The width of the dialog in pixels
4852 </pre>
4853          *
4854          * Example usage:
4855          * <pre><code>
4856 Roo.Msg.show({
4857    title: 'Address',
4858    msg: 'Please enter your address:',
4859    width: 300,
4860    buttons: Roo.MessageBox.OKCANCEL,
4861    multiline: true,
4862    fn: saveAddress,
4863    animEl: 'addAddressBtn'
4864 });
4865 </code></pre>
4866          * @param {Object} config Configuration options
4867          * @return {Roo.MessageBox} This message box
4868          */
4869         show : function(options)
4870         {
4871             
4872             // this causes nightmares if you show one dialog after another
4873             // especially on callbacks..
4874              
4875             if(this.isVisible()){
4876                 
4877                 this.hide();
4878                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4879                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4880                 Roo.log("New Dialog Message:" +  options.msg )
4881                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4882                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4883                 
4884             }
4885             var d = this.getDialog();
4886             opt = options;
4887             d.setTitle(opt.title || "&#160;");
4888             d.closeEl.setDisplayed(opt.closable !== false);
4889             activeTextEl = textboxEl;
4890             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4891             if(opt.prompt){
4892                 if(opt.multiline){
4893                     textboxEl.hide();
4894                     textareaEl.show();
4895                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4896                         opt.multiline : this.defaultTextHeight);
4897                     activeTextEl = textareaEl;
4898                 }else{
4899                     textboxEl.show();
4900                     textareaEl.hide();
4901                 }
4902             }else{
4903                 textboxEl.hide();
4904                 textareaEl.hide();
4905             }
4906             progressEl.setDisplayed(opt.progress === true);
4907             if (opt.progress) {
4908                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4909             }
4910             this.updateProgress(0);
4911             activeTextEl.dom.value = opt.value || "";
4912             if(opt.prompt){
4913                 dlg.setDefaultButton(activeTextEl);
4914             }else{
4915                 var bs = opt.buttons;
4916                 var db = null;
4917                 if(bs && bs.ok){
4918                     db = buttons["ok"];
4919                 }else if(bs && bs.yes){
4920                     db = buttons["yes"];
4921                 }
4922                 dlg.setDefaultButton(db);
4923             }
4924             bwidth = updateButtons(opt.buttons);
4925             this.updateText(opt.msg);
4926             if(opt.cls){
4927                 d.el.addClass(opt.cls);
4928             }
4929             d.proxyDrag = opt.proxyDrag === true;
4930             d.modal = opt.modal !== false;
4931             d.mask = opt.modal !== false ? mask : false;
4932             if(!d.isVisible()){
4933                 // force it to the end of the z-index stack so it gets a cursor in FF
4934                 document.body.appendChild(dlg.el.dom);
4935                 d.animateTarget = null;
4936                 d.show(options.animEl);
4937             }
4938             return this;
4939         },
4940
4941         /**
4942          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4943          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4944          * and closing the message box when the process is complete.
4945          * @param {String} title The title bar text
4946          * @param {String} msg The message box body text
4947          * @return {Roo.MessageBox} This message box
4948          */
4949         progress : function(title, msg){
4950             this.show({
4951                 title : title,
4952                 msg : msg,
4953                 buttons: false,
4954                 progress:true,
4955                 closable:false,
4956                 minWidth: this.minProgressWidth,
4957                 modal : true
4958             });
4959             return this;
4960         },
4961
4962         /**
4963          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4964          * If a callback function is passed it will be called after the user clicks the button, and the
4965          * id of the button that was clicked will be passed as the only parameter to the callback
4966          * (could also be the top-right close button).
4967          * @param {String} title The title bar text
4968          * @param {String} msg The message box body text
4969          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4970          * @param {Object} scope (optional) The scope of the callback function
4971          * @return {Roo.MessageBox} This message box
4972          */
4973         alert : function(title, msg, fn, scope)
4974         {
4975             this.show({
4976                 title : title,
4977                 msg : msg,
4978                 buttons: this.OK,
4979                 fn: fn,
4980                 closable : false,
4981                 scope : scope,
4982                 modal : true
4983             });
4984             return this;
4985         },
4986
4987         /**
4988          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4989          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4990          * You are responsible for closing the message box when the process is complete.
4991          * @param {String} msg The message box body text
4992          * @param {String} title (optional) The title bar text
4993          * @return {Roo.MessageBox} This message box
4994          */
4995         wait : function(msg, title){
4996             this.show({
4997                 title : title,
4998                 msg : msg,
4999                 buttons: false,
5000                 closable:false,
5001                 progress:true,
5002                 modal:true,
5003                 width:300,
5004                 wait:true
5005             });
5006             waitTimer = Roo.TaskMgr.start({
5007                 run: function(i){
5008                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5009                 },
5010                 interval: 1000
5011             });
5012             return this;
5013         },
5014
5015         /**
5016          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5017          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5018          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5019          * @param {String} title The title bar text
5020          * @param {String} msg The message box body text
5021          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5022          * @param {Object} scope (optional) The scope of the callback function
5023          * @return {Roo.MessageBox} This message box
5024          */
5025         confirm : function(title, msg, fn, scope){
5026             this.show({
5027                 title : title,
5028                 msg : msg,
5029                 buttons: this.YESNO,
5030                 fn: fn,
5031                 scope : scope,
5032                 modal : true
5033             });
5034             return this;
5035         },
5036
5037         /**
5038          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5039          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5040          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5041          * (could also be the top-right close button) and the text that was entered will be passed as the two
5042          * parameters to the callback.
5043          * @param {String} title The title bar text
5044          * @param {String} msg The message box body text
5045          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5046          * @param {Object} scope (optional) The scope of the callback function
5047          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5048          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5049          * @return {Roo.MessageBox} This message box
5050          */
5051         prompt : function(title, msg, fn, scope, multiline){
5052             this.show({
5053                 title : title,
5054                 msg : msg,
5055                 buttons: this.OKCANCEL,
5056                 fn: fn,
5057                 minWidth:250,
5058                 scope : scope,
5059                 prompt:true,
5060                 multiline: multiline,
5061                 modal : true
5062             });
5063             return this;
5064         },
5065
5066         /**
5067          * Button config that displays a single OK button
5068          * @type Object
5069          */
5070         OK : {ok:true},
5071         /**
5072          * Button config that displays Yes and No buttons
5073          * @type Object
5074          */
5075         YESNO : {yes:true, no:true},
5076         /**
5077          * Button config that displays OK and Cancel buttons
5078          * @type Object
5079          */
5080         OKCANCEL : {ok:true, cancel:true},
5081         /**
5082          * Button config that displays Yes, No and Cancel buttons
5083          * @type Object
5084          */
5085         YESNOCANCEL : {yes:true, no:true, cancel:true},
5086
5087         /**
5088          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5089          * @type Number
5090          */
5091         defaultTextHeight : 75,
5092         /**
5093          * The maximum width in pixels of the message box (defaults to 600)
5094          * @type Number
5095          */
5096         maxWidth : 600,
5097         /**
5098          * The minimum width in pixels of the message box (defaults to 100)
5099          * @type Number
5100          */
5101         minWidth : 100,
5102         /**
5103          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5104          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5105          * @type Number
5106          */
5107         minProgressWidth : 250,
5108         /**
5109          * An object containing the default button text strings that can be overriden for localized language support.
5110          * Supported properties are: ok, cancel, yes and no.
5111          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5112          * @type Object
5113          */
5114         buttonText : {
5115             ok : "OK",
5116             cancel : "Cancel",
5117             yes : "Yes",
5118             no : "No"
5119         }
5120     };
5121 }();
5122
5123 /**
5124  * Shorthand for {@link Roo.MessageBox}
5125  */
5126 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5127 Roo.Msg = Roo.Msg || Roo.MessageBox;
5128 /*
5129  * - LGPL
5130  *
5131  * navbar
5132  * 
5133  */
5134
5135 /**
5136  * @class Roo.bootstrap.Navbar
5137  * @extends Roo.bootstrap.Component
5138  * Bootstrap Navbar class
5139
5140  * @constructor
5141  * Create a new Navbar
5142  * @param {Object} config The config object
5143  */
5144
5145
5146 Roo.bootstrap.Navbar = function(config){
5147     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5148     this.addEvents({
5149         // raw events
5150         /**
5151          * @event beforetoggle
5152          * Fire before toggle the menu
5153          * @param {Roo.EventObject} e
5154          */
5155         "beforetoggle" : true
5156     });
5157 };
5158
5159 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5160     
5161     
5162    
5163     // private
5164     navItems : false,
5165     loadMask : false,
5166     
5167     
5168     getAutoCreate : function(){
5169         
5170         
5171         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5172         
5173     },
5174     
5175     initEvents :function ()
5176     {
5177         //Roo.log(this.el.select('.navbar-toggle',true));
5178         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5179         
5180         var mark = {
5181             tag: "div",
5182             cls:"x-dlg-mask"
5183         };
5184         
5185         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5186         
5187         var size = this.el.getSize();
5188         this.maskEl.setSize(size.width, size.height);
5189         this.maskEl.enableDisplayMode("block");
5190         this.maskEl.hide();
5191         
5192         if(this.loadMask){
5193             this.maskEl.show();
5194         }
5195     },
5196     
5197     
5198     getChildContainer : function()
5199     {
5200         if (this.el && this.el.select('.collapse').getCount()) {
5201             return this.el.select('.collapse',true).first();
5202         }
5203         
5204         return this.el;
5205     },
5206     
5207     mask : function()
5208     {
5209         this.maskEl.show();
5210     },
5211     
5212     unmask : function()
5213     {
5214         this.maskEl.hide();
5215     },
5216     onToggle : function()
5217     {
5218         
5219         if(this.fireEvent('beforetoggle', this) === false){
5220             return;
5221         }
5222         var ce = this.el.select('.navbar-collapse',true).first();
5223       
5224         if (!ce.hasClass('show')) {
5225            this.expand();
5226         } else {
5227             this.collapse();
5228         }
5229         
5230         
5231     
5232     },
5233     /**
5234      * Expand the navbar pulldown 
5235      */
5236     expand : function ()
5237     {
5238        
5239         var ce = this.el.select('.navbar-collapse',true).first();
5240         if (ce.hasClass('collapsing')) {
5241             return;
5242         }
5243         ce.dom.style.height = '';
5244                // show it...
5245         ce.addClass('in'); // old...
5246         ce.removeClass('collapse');
5247         ce.addClass('show');
5248         var h = ce.getHeight();
5249         Roo.log(h);
5250         ce.removeClass('show');
5251         // at this point we should be able to see it..
5252         ce.addClass('collapsing');
5253         
5254         ce.setHeight(0); // resize it ...
5255         ce.on('transitionend', function() {
5256             //Roo.log('done transition');
5257             ce.removeClass('collapsing');
5258             ce.addClass('show');
5259             ce.removeClass('collapse');
5260
5261             ce.dom.style.height = '';
5262         }, this, { single: true} );
5263         ce.setHeight(h);
5264         ce.dom.scrollTop = 0;
5265     },
5266     /**
5267      * Collapse the navbar pulldown 
5268      */
5269     collapse : function()
5270     {
5271          var ce = this.el.select('.navbar-collapse',true).first();
5272        
5273         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5274             // it's collapsed or collapsing..
5275             return;
5276         }
5277         ce.removeClass('in'); // old...
5278         ce.setHeight(ce.getHeight());
5279         ce.removeClass('show');
5280         ce.addClass('collapsing');
5281         
5282         ce.on('transitionend', function() {
5283             ce.dom.style.height = '';
5284             ce.removeClass('collapsing');
5285             ce.addClass('collapse');
5286         }, this, { single: true} );
5287         ce.setHeight(0);
5288     }
5289     
5290     
5291     
5292 });
5293
5294
5295
5296  
5297
5298  /*
5299  * - LGPL
5300  *
5301  * navbar
5302  * 
5303  */
5304
5305 /**
5306  * @class Roo.bootstrap.NavSimplebar
5307  * @extends Roo.bootstrap.Navbar
5308  * Bootstrap Sidebar class
5309  *
5310  * @cfg {Boolean} inverse is inverted color
5311  * 
5312  * @cfg {String} type (nav | pills | tabs)
5313  * @cfg {Boolean} arrangement stacked | justified
5314  * @cfg {String} align (left | right) alignment
5315  * 
5316  * @cfg {Boolean} main (true|false) main nav bar? default false
5317  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5318  * 
5319  * @cfg {String} tag (header|footer|nav|div) default is nav 
5320
5321  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5322  * 
5323  * 
5324  * @constructor
5325  * Create a new Sidebar
5326  * @param {Object} config The config object
5327  */
5328
5329
5330 Roo.bootstrap.NavSimplebar = function(config){
5331     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5332 };
5333
5334 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5335     
5336     inverse: false,
5337     
5338     type: false,
5339     arrangement: '',
5340     align : false,
5341     
5342     weight : 'light',
5343     
5344     main : false,
5345     
5346     
5347     tag : false,
5348     
5349     
5350     getAutoCreate : function(){
5351         
5352         
5353         var cfg = {
5354             tag : this.tag || 'div',
5355             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5356         };
5357         if (['light','white'].indexOf(this.weight) > -1) {
5358             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5359         }
5360         cfg.cls += ' bg-' + this.weight;
5361         
5362         if (this.inverse) {
5363             cfg.cls += ' navbar-inverse';
5364             
5365         }
5366         
5367         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5368         
5369         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5370             return cfg;
5371         }
5372         
5373         
5374     
5375         
5376         cfg.cn = [
5377             {
5378                 cls: 'nav nav-' + this.xtype,
5379                 tag : 'ul'
5380             }
5381         ];
5382         
5383          
5384         this.type = this.type || 'nav';
5385         if (['tabs','pills'].indexOf(this.type) != -1) {
5386             cfg.cn[0].cls += ' nav-' + this.type
5387         
5388         
5389         } else {
5390             if (this.type!=='nav') {
5391                 Roo.log('nav type must be nav/tabs/pills')
5392             }
5393             cfg.cn[0].cls += ' navbar-nav'
5394         }
5395         
5396         
5397         
5398         
5399         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5400             cfg.cn[0].cls += ' nav-' + this.arrangement;
5401         }
5402         
5403         
5404         if (this.align === 'right') {
5405             cfg.cn[0].cls += ' navbar-right';
5406         }
5407         
5408         
5409         
5410         
5411         return cfg;
5412     
5413         
5414     }
5415     
5416     
5417     
5418 });
5419
5420
5421
5422  
5423
5424  
5425        /*
5426  * - LGPL
5427  *
5428  * navbar
5429  * navbar-fixed-top
5430  * navbar-expand-md  fixed-top 
5431  */
5432
5433 /**
5434  * @class Roo.bootstrap.NavHeaderbar
5435  * @extends Roo.bootstrap.NavSimplebar
5436  * Bootstrap Sidebar class
5437  *
5438  * @cfg {String} brand what is brand
5439  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5440  * @cfg {String} brand_href href of the brand
5441  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5442  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5443  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5444  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5445  * 
5446  * @constructor
5447  * Create a new Sidebar
5448  * @param {Object} config The config object
5449  */
5450
5451
5452 Roo.bootstrap.NavHeaderbar = function(config){
5453     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5454       
5455 };
5456
5457 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5458     
5459     position: '',
5460     brand: '',
5461     brand_href: false,
5462     srButton : true,
5463     autohide : false,
5464     desktopCenter : false,
5465    
5466     
5467     getAutoCreate : function(){
5468         
5469         var   cfg = {
5470             tag: this.nav || 'nav',
5471             cls: 'navbar navbar-expand-md',
5472             role: 'navigation',
5473             cn: []
5474         };
5475         
5476         var cn = cfg.cn;
5477         if (this.desktopCenter) {
5478             cn.push({cls : 'container', cn : []});
5479             cn = cn[0].cn;
5480         }
5481         
5482         if(this.srButton){
5483             var btn = {
5484                 tag: 'button',
5485                 type: 'button',
5486                 cls: 'navbar-toggle navbar-toggler',
5487                 'data-toggle': 'collapse',
5488                 cn: [
5489                     {
5490                         tag: 'span',
5491                         cls: 'sr-only',
5492                         html: 'Toggle navigation'
5493                     },
5494                     {
5495                         tag: 'span',
5496                         cls: 'icon-bar navbar-toggler-icon'
5497                     },
5498                     {
5499                         tag: 'span',
5500                         cls: 'icon-bar'
5501                     },
5502                     {
5503                         tag: 'span',
5504                         cls: 'icon-bar'
5505                     }
5506                 ]
5507             };
5508             
5509             cn.push( Roo.bootstrap.version == 4 ? btn : {
5510                 tag: 'div',
5511                 cls: 'navbar-header',
5512                 cn: [
5513                     btn
5514                 ]
5515             });
5516         }
5517         
5518         cn.push({
5519             tag: 'div',
5520             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5521             cn : []
5522         });
5523         
5524         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5525         
5526         if (['light','white'].indexOf(this.weight) > -1) {
5527             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5528         }
5529         cfg.cls += ' bg-' + this.weight;
5530         
5531         
5532         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5533             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5534             
5535             // tag can override this..
5536             
5537             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5538         }
5539         
5540         if (this.brand !== '') {
5541             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5542             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5543                 tag: 'a',
5544                 href: this.brand_href ? this.brand_href : '#',
5545                 cls: 'navbar-brand',
5546                 cn: [
5547                 this.brand
5548                 ]
5549             });
5550         }
5551         
5552         if(this.main){
5553             cfg.cls += ' main-nav';
5554         }
5555         
5556         
5557         return cfg;
5558
5559         
5560     },
5561     getHeaderChildContainer : function()
5562     {
5563         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5564             return this.el.select('.navbar-header',true).first();
5565         }
5566         
5567         return this.getChildContainer();
5568     },
5569     
5570     getChildContainer : function()
5571     {
5572          
5573         return this.el.select('.roo-navbar-collapse',true).first();
5574          
5575         
5576     },
5577     
5578     initEvents : function()
5579     {
5580         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5581         
5582         if (this.autohide) {
5583             
5584             var prevScroll = 0;
5585             var ft = this.el;
5586             
5587             Roo.get(document).on('scroll',function(e) {
5588                 var ns = Roo.get(document).getScroll().top;
5589                 var os = prevScroll;
5590                 prevScroll = ns;
5591                 
5592                 if(ns > os){
5593                     ft.removeClass('slideDown');
5594                     ft.addClass('slideUp');
5595                     return;
5596                 }
5597                 ft.removeClass('slideUp');
5598                 ft.addClass('slideDown');
5599                  
5600               
5601           },this);
5602         }
5603     }    
5604     
5605 });
5606
5607
5608
5609  
5610
5611  /*
5612  * - LGPL
5613  *
5614  * navbar
5615  * 
5616  */
5617
5618 /**
5619  * @class Roo.bootstrap.NavSidebar
5620  * @extends Roo.bootstrap.Navbar
5621  * Bootstrap Sidebar class
5622  * 
5623  * @constructor
5624  * Create a new Sidebar
5625  * @param {Object} config The config object
5626  */
5627
5628
5629 Roo.bootstrap.NavSidebar = function(config){
5630     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5631 };
5632
5633 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5634     
5635     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5636     
5637     getAutoCreate : function(){
5638         
5639         
5640         return  {
5641             tag: 'div',
5642             cls: 'sidebar sidebar-nav'
5643         };
5644     
5645         
5646     }
5647     
5648     
5649     
5650 });
5651
5652
5653
5654  
5655
5656  /*
5657  * - LGPL
5658  *
5659  * nav group
5660  * 
5661  */
5662
5663 /**
5664  * @class Roo.bootstrap.NavGroup
5665  * @extends Roo.bootstrap.Component
5666  * Bootstrap NavGroup class
5667  * @cfg {String} align (left|right)
5668  * @cfg {Boolean} inverse
5669  * @cfg {String} type (nav|pills|tab) default nav
5670  * @cfg {String} navId - reference Id for navbar.
5671
5672  * 
5673  * @constructor
5674  * Create a new nav group
5675  * @param {Object} config The config object
5676  */
5677
5678 Roo.bootstrap.NavGroup = function(config){
5679     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5680     this.navItems = [];
5681    
5682     Roo.bootstrap.NavGroup.register(this);
5683      this.addEvents({
5684         /**
5685              * @event changed
5686              * Fires when the active item changes
5687              * @param {Roo.bootstrap.NavGroup} this
5688              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5689              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5690          */
5691         'changed': true
5692      });
5693     
5694 };
5695
5696 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5697     
5698     align: '',
5699     inverse: false,
5700     form: false,
5701     type: 'nav',
5702     navId : '',
5703     // private
5704     
5705     navItems : false, 
5706     
5707     getAutoCreate : function()
5708     {
5709         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5710         
5711         cfg = {
5712             tag : 'ul',
5713             cls: 'nav' 
5714         };
5715         if (Roo.bootstrap.version == 4) {
5716             if (['tabs','pills'].indexOf(this.type) != -1) {
5717                 cfg.cls += ' nav-' + this.type; 
5718             } else {
5719                 // trying to remove so header bar can right align top?
5720                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5721                     // do not use on header bar... 
5722                     cfg.cls += ' navbar-nav';
5723                 }
5724             }
5725             
5726         } else {
5727             if (['tabs','pills'].indexOf(this.type) != -1) {
5728                 cfg.cls += ' nav-' + this.type
5729             } else {
5730                 if (this.type !== 'nav') {
5731                     Roo.log('nav type must be nav/tabs/pills')
5732                 }
5733                 cfg.cls += ' navbar-nav'
5734             }
5735         }
5736         
5737         if (this.parent() && this.parent().sidebar) {
5738             cfg = {
5739                 tag: 'ul',
5740                 cls: 'dashboard-menu sidebar-menu'
5741             };
5742             
5743             return cfg;
5744         }
5745         
5746         if (this.form === true) {
5747             cfg = {
5748                 tag: 'form',
5749                 cls: 'navbar-form form-inline'
5750             };
5751             //nav navbar-right ml-md-auto
5752             if (this.align === 'right') {
5753                 cfg.cls += ' navbar-right ml-md-auto';
5754             } else {
5755                 cfg.cls += ' navbar-left';
5756             }
5757         }
5758         
5759         if (this.align === 'right') {
5760             cfg.cls += ' navbar-right ml-md-auto';
5761         } else {
5762             cfg.cls += ' mr-auto';
5763         }
5764         
5765         if (this.inverse) {
5766             cfg.cls += ' navbar-inverse';
5767             
5768         }
5769         
5770         
5771         return cfg;
5772     },
5773     /**
5774     * sets the active Navigation item
5775     * @param {Roo.bootstrap.NavItem} the new current navitem
5776     */
5777     setActiveItem : function(item)
5778     {
5779         var prev = false;
5780         Roo.each(this.navItems, function(v){
5781             if (v == item) {
5782                 return ;
5783             }
5784             if (v.isActive()) {
5785                 v.setActive(false, true);
5786                 prev = v;
5787                 
5788             }
5789             
5790         });
5791
5792         item.setActive(true, true);
5793         this.fireEvent('changed', this, item, prev);
5794         
5795         
5796     },
5797     /**
5798     * gets the active Navigation item
5799     * @return {Roo.bootstrap.NavItem} the current navitem
5800     */
5801     getActive : function()
5802     {
5803         
5804         var prev = false;
5805         Roo.each(this.navItems, function(v){
5806             
5807             if (v.isActive()) {
5808                 prev = v;
5809                 
5810             }
5811             
5812         });
5813         return prev;
5814     },
5815     
5816     indexOfNav : function()
5817     {
5818         
5819         var prev = false;
5820         Roo.each(this.navItems, function(v,i){
5821             
5822             if (v.isActive()) {
5823                 prev = i;
5824                 
5825             }
5826             
5827         });
5828         return prev;
5829     },
5830     /**
5831     * adds a Navigation item
5832     * @param {Roo.bootstrap.NavItem} the navitem to add
5833     */
5834     addItem : function(cfg)
5835     {
5836         if (this.form && Roo.bootstrap.version == 4) {
5837             cfg.tag = 'div';
5838         }
5839         var cn = new Roo.bootstrap.NavItem(cfg);
5840         this.register(cn);
5841         cn.parentId = this.id;
5842         cn.onRender(this.el, null);
5843         return cn;
5844     },
5845     /**
5846     * register a Navigation item
5847     * @param {Roo.bootstrap.NavItem} the navitem to add
5848     */
5849     register : function(item)
5850     {
5851         this.navItems.push( item);
5852         item.navId = this.navId;
5853     
5854     },
5855     
5856     /**
5857     * clear all the Navigation item
5858     */
5859    
5860     clearAll : function()
5861     {
5862         this.navItems = [];
5863         this.el.dom.innerHTML = '';
5864     },
5865     
5866     getNavItem: function(tabId)
5867     {
5868         var ret = false;
5869         Roo.each(this.navItems, function(e) {
5870             if (e.tabId == tabId) {
5871                ret =  e;
5872                return false;
5873             }
5874             return true;
5875             
5876         });
5877         return ret;
5878     },
5879     
5880     setActiveNext : function()
5881     {
5882         var i = this.indexOfNav(this.getActive());
5883         if (i > this.navItems.length) {
5884             return;
5885         }
5886         this.setActiveItem(this.navItems[i+1]);
5887     },
5888     setActivePrev : function()
5889     {
5890         var i = this.indexOfNav(this.getActive());
5891         if (i  < 1) {
5892             return;
5893         }
5894         this.setActiveItem(this.navItems[i-1]);
5895     },
5896     clearWasActive : function(except) {
5897         Roo.each(this.navItems, function(e) {
5898             if (e.tabId != except.tabId && e.was_active) {
5899                e.was_active = false;
5900                return false;
5901             }
5902             return true;
5903             
5904         });
5905     },
5906     getWasActive : function ()
5907     {
5908         var r = false;
5909         Roo.each(this.navItems, function(e) {
5910             if (e.was_active) {
5911                r = e;
5912                return false;
5913             }
5914             return true;
5915             
5916         });
5917         return r;
5918     }
5919     
5920     
5921 });
5922
5923  
5924 Roo.apply(Roo.bootstrap.NavGroup, {
5925     
5926     groups: {},
5927      /**
5928     * register a Navigation Group
5929     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5930     */
5931     register : function(navgrp)
5932     {
5933         this.groups[navgrp.navId] = navgrp;
5934         
5935     },
5936     /**
5937     * fetch a Navigation Group based on the navigation ID
5938     * @param {string} the navgroup to add
5939     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5940     */
5941     get: function(navId) {
5942         if (typeof(this.groups[navId]) == 'undefined') {
5943             return false;
5944             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5945         }
5946         return this.groups[navId] ;
5947     }
5948     
5949     
5950     
5951 });
5952
5953  /*
5954  * - LGPL
5955  *
5956  * row
5957  * 
5958  */
5959
5960 /**
5961  * @class Roo.bootstrap.NavItem
5962  * @extends Roo.bootstrap.Component
5963  * Bootstrap Navbar.NavItem class
5964  * @cfg {String} href  link to
5965  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5966
5967  * @cfg {String} html content of button
5968  * @cfg {String} badge text inside badge
5969  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5970  * @cfg {String} glyphicon DEPRICATED - use fa
5971  * @cfg {String} icon DEPRICATED - use fa
5972  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5973  * @cfg {Boolean} active Is item active
5974  * @cfg {Boolean} disabled Is item disabled
5975  
5976  * @cfg {Boolean} preventDefault (true | false) default false
5977  * @cfg {String} tabId the tab that this item activates.
5978  * @cfg {String} tagtype (a|span) render as a href or span?
5979  * @cfg {Boolean} animateRef (true|false) link to element default false  
5980   
5981  * @constructor
5982  * Create a new Navbar Item
5983  * @param {Object} config The config object
5984  */
5985 Roo.bootstrap.NavItem = function(config){
5986     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5987     this.addEvents({
5988         // raw events
5989         /**
5990          * @event click
5991          * The raw click event for the entire grid.
5992          * @param {Roo.EventObject} e
5993          */
5994         "click" : true,
5995          /**
5996             * @event changed
5997             * Fires when the active item active state changes
5998             * @param {Roo.bootstrap.NavItem} this
5999             * @param {boolean} state the new state
6000              
6001          */
6002         'changed': true,
6003         /**
6004             * @event scrollto
6005             * Fires when scroll to element
6006             * @param {Roo.bootstrap.NavItem} this
6007             * @param {Object} options
6008             * @param {Roo.EventObject} e
6009              
6010          */
6011         'scrollto': true
6012     });
6013    
6014 };
6015
6016 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6017     
6018     href: false,
6019     html: '',
6020     badge: '',
6021     icon: false,
6022     fa : false,
6023     glyphicon: false,
6024     active: false,
6025     preventDefault : false,
6026     tabId : false,
6027     tagtype : 'a',
6028     tag: 'li',
6029     disabled : false,
6030     animateRef : false,
6031     was_active : false,
6032     button_weight : '',
6033     button_outline : false,
6034     
6035     navLink: false,
6036     
6037     getAutoCreate : function(){
6038          
6039         var cfg = {
6040             tag: this.tag,
6041             cls: 'nav-item'
6042         };
6043         
6044         if (this.active) {
6045             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6046         }
6047         if (this.disabled) {
6048             cfg.cls += ' disabled';
6049         }
6050         
6051         // BS4 only?
6052         if (this.button_weight.length) {
6053             cfg.tag = this.href ? 'a' : 'button';
6054             cfg.html = this.html || '';
6055             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6056             if (this.href) {
6057                 cfg.href = this.href;
6058             }
6059             if (this.fa) {
6060                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6061             }
6062             
6063             // menu .. should add dropdown-menu class - so no need for carat..
6064             
6065             if (this.badge !== '') {
6066                  
6067                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6068             }
6069             return cfg;
6070         }
6071         
6072         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6073             cfg.cn = [
6074                 {
6075                     tag: this.tagtype,
6076                     href : this.href || "#",
6077                     html: this.html || ''
6078                 }
6079             ];
6080             if (this.tagtype == 'a') {
6081                 cfg.cn[0].cls = 'nav-link';
6082             }
6083             if (this.icon) {
6084                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6085             }
6086             if (this.fa) {
6087                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6088             }
6089             if(this.glyphicon) {
6090                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6091             }
6092             
6093             if (this.menu) {
6094                 
6095                 cfg.cn[0].html += " <span class='caret'></span>";
6096              
6097             }
6098             
6099             if (this.badge !== '') {
6100                  
6101                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6102             }
6103         }
6104         
6105         
6106         
6107         return cfg;
6108     },
6109     onRender : function(ct, position)
6110     {
6111        // Roo.log("Call onRender: " + this.xtype);
6112         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6113             this.tag = 'div';
6114         }
6115         
6116         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6117         this.navLink = this.el.select('.nav-link',true).first();
6118         return ret;
6119     },
6120       
6121     
6122     initEvents: function() 
6123     {
6124         if (typeof (this.menu) != 'undefined') {
6125             this.menu.parentType = this.xtype;
6126             this.menu.triggerEl = this.el;
6127             this.menu = this.addxtype(Roo.apply({}, this.menu));
6128         }
6129         
6130         this.el.select('a',true).on('click', this.onClick, this);
6131         
6132         if(this.tagtype == 'span'){
6133             this.el.select('span',true).on('click', this.onClick, this);
6134         }
6135        
6136         // at this point parent should be available..
6137         this.parent().register(this);
6138     },
6139     
6140     onClick : function(e)
6141     {
6142         if (e.getTarget('.dropdown-menu-item')) {
6143             // did you click on a menu itemm.... - then don't trigger onclick..
6144             return;
6145         }
6146         
6147         if(
6148                 this.preventDefault || 
6149                 this.href == '#' 
6150         ){
6151             Roo.log("NavItem - prevent Default?");
6152             e.preventDefault();
6153         }
6154         
6155         if (this.disabled) {
6156             return;
6157         }
6158         
6159         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6160         if (tg && tg.transition) {
6161             Roo.log("waiting for the transitionend");
6162             return;
6163         }
6164         
6165         
6166         
6167         //Roo.log("fire event clicked");
6168         if(this.fireEvent('click', this, e) === false){
6169             return;
6170         };
6171         
6172         if(this.tagtype == 'span'){
6173             return;
6174         }
6175         
6176         //Roo.log(this.href);
6177         var ael = this.el.select('a',true).first();
6178         //Roo.log(ael);
6179         
6180         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6181             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6182             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6183                 return; // ignore... - it's a 'hash' to another page.
6184             }
6185             Roo.log("NavItem - prevent Default?");
6186             e.preventDefault();
6187             this.scrollToElement(e);
6188         }
6189         
6190         
6191         var p =  this.parent();
6192    
6193         if (['tabs','pills'].indexOf(p.type)!==-1) {
6194             if (typeof(p.setActiveItem) !== 'undefined') {
6195                 p.setActiveItem(this);
6196             }
6197         }
6198         
6199         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6200         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6201             // remove the collapsed menu expand...
6202             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6203         }
6204     },
6205     
6206     isActive: function () {
6207         return this.active
6208     },
6209     setActive : function(state, fire, is_was_active)
6210     {
6211         if (this.active && !state && this.navId) {
6212             this.was_active = true;
6213             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6214             if (nv) {
6215                 nv.clearWasActive(this);
6216             }
6217             
6218         }
6219         this.active = state;
6220         
6221         if (!state ) {
6222             this.el.removeClass('active');
6223             this.navLink ? this.navLink.removeClass('active') : false;
6224         } else if (!this.el.hasClass('active')) {
6225             
6226             this.el.addClass('active');
6227             if (Roo.bootstrap.version == 4 && this.navLink ) {
6228                 this.navLink.addClass('active');
6229             }
6230             
6231         }
6232         if (fire) {
6233             this.fireEvent('changed', this, state);
6234         }
6235         
6236         // show a panel if it's registered and related..
6237         
6238         if (!this.navId || !this.tabId || !state || is_was_active) {
6239             return;
6240         }
6241         
6242         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6243         if (!tg) {
6244             return;
6245         }
6246         var pan = tg.getPanelByName(this.tabId);
6247         if (!pan) {
6248             return;
6249         }
6250         // if we can not flip to new panel - go back to old nav highlight..
6251         if (false == tg.showPanel(pan)) {
6252             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6253             if (nv) {
6254                 var onav = nv.getWasActive();
6255                 if (onav) {
6256                     onav.setActive(true, false, true);
6257                 }
6258             }
6259             
6260         }
6261         
6262         
6263         
6264     },
6265      // this should not be here...
6266     setDisabled : function(state)
6267     {
6268         this.disabled = state;
6269         if (!state ) {
6270             this.el.removeClass('disabled');
6271         } else if (!this.el.hasClass('disabled')) {
6272             this.el.addClass('disabled');
6273         }
6274         
6275     },
6276     
6277     /**
6278      * Fetch the element to display the tooltip on.
6279      * @return {Roo.Element} defaults to this.el
6280      */
6281     tooltipEl : function()
6282     {
6283         return this.el.select('' + this.tagtype + '', true).first();
6284     },
6285     
6286     scrollToElement : function(e)
6287     {
6288         var c = document.body;
6289         
6290         /*
6291          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6292          */
6293         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6294             c = document.documentElement;
6295         }
6296         
6297         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6298         
6299         if(!target){
6300             return;
6301         }
6302
6303         var o = target.calcOffsetsTo(c);
6304         
6305         var options = {
6306             target : target,
6307             value : o[1]
6308         };
6309         
6310         this.fireEvent('scrollto', this, options, e);
6311         
6312         Roo.get(c).scrollTo('top', options.value, true);
6313         
6314         return;
6315     }
6316 });
6317  
6318
6319  /*
6320  * - LGPL
6321  *
6322  * sidebar item
6323  *
6324  *  li
6325  *    <span> icon </span>
6326  *    <span> text </span>
6327  *    <span>badge </span>
6328  */
6329
6330 /**
6331  * @class Roo.bootstrap.NavSidebarItem
6332  * @extends Roo.bootstrap.NavItem
6333  * Bootstrap Navbar.NavSidebarItem class
6334  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6335  * {Boolean} open is the menu open
6336  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6337  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6338  * {String} buttonSize (sm|md|lg)the extra classes for the button
6339  * {Boolean} showArrow show arrow next to the text (default true)
6340  * @constructor
6341  * Create a new Navbar Button
6342  * @param {Object} config The config object
6343  */
6344 Roo.bootstrap.NavSidebarItem = function(config){
6345     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6346     this.addEvents({
6347         // raw events
6348         /**
6349          * @event click
6350          * The raw click event for the entire grid.
6351          * @param {Roo.EventObject} e
6352          */
6353         "click" : true,
6354          /**
6355             * @event changed
6356             * Fires when the active item active state changes
6357             * @param {Roo.bootstrap.NavSidebarItem} this
6358             * @param {boolean} state the new state
6359              
6360          */
6361         'changed': true
6362     });
6363    
6364 };
6365
6366 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6367     
6368     badgeWeight : 'default',
6369     
6370     open: false,
6371     
6372     buttonView : false,
6373     
6374     buttonWeight : 'default',
6375     
6376     buttonSize : 'md',
6377     
6378     showArrow : true,
6379     
6380     getAutoCreate : function(){
6381         
6382         
6383         var a = {
6384                 tag: 'a',
6385                 href : this.href || '#',
6386                 cls: '',
6387                 html : '',
6388                 cn : []
6389         };
6390         
6391         if(this.buttonView){
6392             a = {
6393                 tag: 'button',
6394                 href : this.href || '#',
6395                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6396                 html : this.html,
6397                 cn : []
6398             };
6399         }
6400         
6401         var cfg = {
6402             tag: 'li',
6403             cls: '',
6404             cn: [ a ]
6405         };
6406         
6407         if (this.active) {
6408             cfg.cls += ' active';
6409         }
6410         
6411         if (this.disabled) {
6412             cfg.cls += ' disabled';
6413         }
6414         if (this.open) {
6415             cfg.cls += ' open x-open';
6416         }
6417         // left icon..
6418         if (this.glyphicon || this.icon) {
6419             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6420             a.cn.push({ tag : 'i', cls : c }) ;
6421         }
6422         
6423         if(!this.buttonView){
6424             var span = {
6425                 tag: 'span',
6426                 html : this.html || ''
6427             };
6428
6429             a.cn.push(span);
6430             
6431         }
6432         
6433         if (this.badge !== '') {
6434             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6435         }
6436         
6437         if (this.menu) {
6438             
6439             if(this.showArrow){
6440                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6441             }
6442             
6443             a.cls += ' dropdown-toggle treeview' ;
6444         }
6445         
6446         return cfg;
6447     },
6448     
6449     initEvents : function()
6450     { 
6451         if (typeof (this.menu) != 'undefined') {
6452             this.menu.parentType = this.xtype;
6453             this.menu.triggerEl = this.el;
6454             this.menu = this.addxtype(Roo.apply({}, this.menu));
6455         }
6456         
6457         this.el.on('click', this.onClick, this);
6458         
6459         if(this.badge !== ''){
6460             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6461         }
6462         
6463     },
6464     
6465     onClick : function(e)
6466     {
6467         if(this.disabled){
6468             e.preventDefault();
6469             return;
6470         }
6471         
6472         if(this.preventDefault){
6473             e.preventDefault();
6474         }
6475         
6476         this.fireEvent('click', this, e);
6477     },
6478     
6479     disable : function()
6480     {
6481         this.setDisabled(true);
6482     },
6483     
6484     enable : function()
6485     {
6486         this.setDisabled(false);
6487     },
6488     
6489     setDisabled : function(state)
6490     {
6491         if(this.disabled == state){
6492             return;
6493         }
6494         
6495         this.disabled = state;
6496         
6497         if (state) {
6498             this.el.addClass('disabled');
6499             return;
6500         }
6501         
6502         this.el.removeClass('disabled');
6503         
6504         return;
6505     },
6506     
6507     setActive : function(state)
6508     {
6509         if(this.active == state){
6510             return;
6511         }
6512         
6513         this.active = state;
6514         
6515         if (state) {
6516             this.el.addClass('active');
6517             return;
6518         }
6519         
6520         this.el.removeClass('active');
6521         
6522         return;
6523     },
6524     
6525     isActive: function () 
6526     {
6527         return this.active;
6528     },
6529     
6530     setBadge : function(str)
6531     {
6532         if(!this.badgeEl){
6533             return;
6534         }
6535         
6536         this.badgeEl.dom.innerHTML = str;
6537     }
6538     
6539    
6540      
6541  
6542 });
6543  
6544
6545  /*
6546  * - LGPL
6547  *
6548  * row
6549  * 
6550  */
6551
6552 /**
6553  * @class Roo.bootstrap.Row
6554  * @extends Roo.bootstrap.Component
6555  * Bootstrap Row class (contains columns...)
6556  * 
6557  * @constructor
6558  * Create a new Row
6559  * @param {Object} config The config object
6560  */
6561
6562 Roo.bootstrap.Row = function(config){
6563     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6564 };
6565
6566 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6567     
6568     getAutoCreate : function(){
6569        return {
6570             cls: 'row clearfix'
6571        };
6572     }
6573     
6574     
6575 });
6576
6577  
6578
6579  /*
6580  * - LGPL
6581  *
6582  * pagination
6583  * 
6584  */
6585
6586 /**
6587  * @class Roo.bootstrap.Pagination
6588  * @extends Roo.bootstrap.Component
6589  * Bootstrap Pagination class
6590  * @cfg {String} size xs | sm | md | lg
6591  * @cfg {Boolean} inverse false | true
6592  * 
6593  * @constructor
6594  * Create a new Pagination
6595  * @param {Object} config The config object
6596  */
6597
6598 Roo.bootstrap.Pagination = function(config){
6599     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6600 };
6601
6602 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6603     
6604     cls: false,
6605     size: false,
6606     inverse: false,
6607     
6608     getAutoCreate : function(){
6609         var cfg = {
6610             tag: 'ul',
6611                 cls: 'pagination'
6612         };
6613         if (this.inverse) {
6614             cfg.cls += ' inverse';
6615         }
6616         if (this.html) {
6617             cfg.html=this.html;
6618         }
6619         if (this.cls) {
6620             cfg.cls += " " + this.cls;
6621         }
6622         return cfg;
6623     }
6624    
6625 });
6626
6627  
6628
6629  /*
6630  * - LGPL
6631  *
6632  * Pagination item
6633  * 
6634  */
6635
6636
6637 /**
6638  * @class Roo.bootstrap.PaginationItem
6639  * @extends Roo.bootstrap.Component
6640  * Bootstrap PaginationItem class
6641  * @cfg {String} html text
6642  * @cfg {String} href the link
6643  * @cfg {Boolean} preventDefault (true | false) default true
6644  * @cfg {Boolean} active (true | false) default false
6645  * @cfg {Boolean} disabled default false
6646  * 
6647  * 
6648  * @constructor
6649  * Create a new PaginationItem
6650  * @param {Object} config The config object
6651  */
6652
6653
6654 Roo.bootstrap.PaginationItem = function(config){
6655     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6656     this.addEvents({
6657         // raw events
6658         /**
6659          * @event click
6660          * The raw click event for the entire grid.
6661          * @param {Roo.EventObject} e
6662          */
6663         "click" : true
6664     });
6665 };
6666
6667 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6668     
6669     href : false,
6670     html : false,
6671     preventDefault: true,
6672     active : false,
6673     cls : false,
6674     disabled: false,
6675     
6676     getAutoCreate : function(){
6677         var cfg= {
6678             tag: 'li',
6679             cn: [
6680                 {
6681                     tag : 'a',
6682                     href : this.href ? this.href : '#',
6683                     html : this.html ? this.html : ''
6684                 }
6685             ]
6686         };
6687         
6688         if(this.cls){
6689             cfg.cls = this.cls;
6690         }
6691         
6692         if(this.disabled){
6693             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6694         }
6695         
6696         if(this.active){
6697             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6698         }
6699         
6700         return cfg;
6701     },
6702     
6703     initEvents: function() {
6704         
6705         this.el.on('click', this.onClick, this);
6706         
6707     },
6708     onClick : function(e)
6709     {
6710         Roo.log('PaginationItem on click ');
6711         if(this.preventDefault){
6712             e.preventDefault();
6713         }
6714         
6715         if(this.disabled){
6716             return;
6717         }
6718         
6719         this.fireEvent('click', this, e);
6720     }
6721    
6722 });
6723
6724  
6725
6726  /*
6727  * - LGPL
6728  *
6729  * slider
6730  * 
6731  */
6732
6733
6734 /**
6735  * @class Roo.bootstrap.Slider
6736  * @extends Roo.bootstrap.Component
6737  * Bootstrap Slider class
6738  *    
6739  * @constructor
6740  * Create a new Slider
6741  * @param {Object} config The config object
6742  */
6743
6744 Roo.bootstrap.Slider = function(config){
6745     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6746 };
6747
6748 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6749     
6750     getAutoCreate : function(){
6751         
6752         var cfg = {
6753             tag: 'div',
6754             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6755             cn: [
6756                 {
6757                     tag: 'a',
6758                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6759                 }
6760             ]
6761         };
6762         
6763         return cfg;
6764     }
6765    
6766 });
6767
6768  /*
6769  * Based on:
6770  * Ext JS Library 1.1.1
6771  * Copyright(c) 2006-2007, Ext JS, LLC.
6772  *
6773  * Originally Released Under LGPL - original licence link has changed is not relivant.
6774  *
6775  * Fork - LGPL
6776  * <script type="text/javascript">
6777  */
6778  
6779
6780 /**
6781  * @class Roo.grid.ColumnModel
6782  * @extends Roo.util.Observable
6783  * This is the default implementation of a ColumnModel used by the Grid. It defines
6784  * the columns in the grid.
6785  * <br>Usage:<br>
6786  <pre><code>
6787  var colModel = new Roo.grid.ColumnModel([
6788         {header: "Ticker", width: 60, sortable: true, locked: true},
6789         {header: "Company Name", width: 150, sortable: true},
6790         {header: "Market Cap.", width: 100, sortable: true},
6791         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6792         {header: "Employees", width: 100, sortable: true, resizable: false}
6793  ]);
6794  </code></pre>
6795  * <p>
6796  
6797  * The config options listed for this class are options which may appear in each
6798  * individual column definition.
6799  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6800  * @constructor
6801  * @param {Object} config An Array of column config objects. See this class's
6802  * config objects for details.
6803 */
6804 Roo.grid.ColumnModel = function(config){
6805         /**
6806      * The config passed into the constructor
6807      */
6808     this.config = config;
6809     this.lookup = {};
6810
6811     // if no id, create one
6812     // if the column does not have a dataIndex mapping,
6813     // map it to the order it is in the config
6814     for(var i = 0, len = config.length; i < len; i++){
6815         var c = config[i];
6816         if(typeof c.dataIndex == "undefined"){
6817             c.dataIndex = i;
6818         }
6819         if(typeof c.renderer == "string"){
6820             c.renderer = Roo.util.Format[c.renderer];
6821         }
6822         if(typeof c.id == "undefined"){
6823             c.id = Roo.id();
6824         }
6825         if(c.editor && c.editor.xtype){
6826             c.editor  = Roo.factory(c.editor, Roo.grid);
6827         }
6828         if(c.editor && c.editor.isFormField){
6829             c.editor = new Roo.grid.GridEditor(c.editor);
6830         }
6831         this.lookup[c.id] = c;
6832     }
6833
6834     /**
6835      * The width of columns which have no width specified (defaults to 100)
6836      * @type Number
6837      */
6838     this.defaultWidth = 100;
6839
6840     /**
6841      * Default sortable of columns which have no sortable specified (defaults to false)
6842      * @type Boolean
6843      */
6844     this.defaultSortable = false;
6845
6846     this.addEvents({
6847         /**
6848              * @event widthchange
6849              * Fires when the width of a column changes.
6850              * @param {ColumnModel} this
6851              * @param {Number} columnIndex The column index
6852              * @param {Number} newWidth The new width
6853              */
6854             "widthchange": true,
6855         /**
6856              * @event headerchange
6857              * Fires when the text of a header changes.
6858              * @param {ColumnModel} this
6859              * @param {Number} columnIndex The column index
6860              * @param {Number} newText The new header text
6861              */
6862             "headerchange": true,
6863         /**
6864              * @event hiddenchange
6865              * Fires when a column is hidden or "unhidden".
6866              * @param {ColumnModel} this
6867              * @param {Number} columnIndex The column index
6868              * @param {Boolean} hidden true if hidden, false otherwise
6869              */
6870             "hiddenchange": true,
6871             /**
6872          * @event columnmoved
6873          * Fires when a column is moved.
6874          * @param {ColumnModel} this
6875          * @param {Number} oldIndex
6876          * @param {Number} newIndex
6877          */
6878         "columnmoved" : true,
6879         /**
6880          * @event columlockchange
6881          * Fires when a column's locked state is changed
6882          * @param {ColumnModel} this
6883          * @param {Number} colIndex
6884          * @param {Boolean} locked true if locked
6885          */
6886         "columnlockchange" : true
6887     });
6888     Roo.grid.ColumnModel.superclass.constructor.call(this);
6889 };
6890 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6891     /**
6892      * @cfg {String} header The header text to display in the Grid view.
6893      */
6894     /**
6895      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6896      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6897      * specified, the column's index is used as an index into the Record's data Array.
6898      */
6899     /**
6900      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6901      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6902      */
6903     /**
6904      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6905      * Defaults to the value of the {@link #defaultSortable} property.
6906      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6907      */
6908     /**
6909      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6910      */
6911     /**
6912      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6913      */
6914     /**
6915      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6916      */
6917     /**
6918      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6919      */
6920     /**
6921      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6922      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6923      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6924      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6925      */
6926        /**
6927      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6928      */
6929     /**
6930      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6931      */
6932     /**
6933      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6934      */
6935     /**
6936      * @cfg {String} cursor (Optional)
6937      */
6938     /**
6939      * @cfg {String} tooltip (Optional)
6940      */
6941     /**
6942      * @cfg {Number} xs (Optional)
6943      */
6944     /**
6945      * @cfg {Number} sm (Optional)
6946      */
6947     /**
6948      * @cfg {Number} md (Optional)
6949      */
6950     /**
6951      * @cfg {Number} lg (Optional)
6952      */
6953     /**
6954      * Returns the id of the column at the specified index.
6955      * @param {Number} index The column index
6956      * @return {String} the id
6957      */
6958     getColumnId : function(index){
6959         return this.config[index].id;
6960     },
6961
6962     /**
6963      * Returns the column for a specified id.
6964      * @param {String} id The column id
6965      * @return {Object} the column
6966      */
6967     getColumnById : function(id){
6968         return this.lookup[id];
6969     },
6970
6971     
6972     /**
6973      * Returns the column for a specified dataIndex.
6974      * @param {String} dataIndex The column dataIndex
6975      * @return {Object|Boolean} the column or false if not found
6976      */
6977     getColumnByDataIndex: function(dataIndex){
6978         var index = this.findColumnIndex(dataIndex);
6979         return index > -1 ? this.config[index] : false;
6980     },
6981     
6982     /**
6983      * Returns the index for a specified column id.
6984      * @param {String} id The column id
6985      * @return {Number} the index, or -1 if not found
6986      */
6987     getIndexById : function(id){
6988         for(var i = 0, len = this.config.length; i < len; i++){
6989             if(this.config[i].id == id){
6990                 return i;
6991             }
6992         }
6993         return -1;
6994     },
6995     
6996     /**
6997      * Returns the index for a specified column dataIndex.
6998      * @param {String} dataIndex The column dataIndex
6999      * @return {Number} the index, or -1 if not found
7000      */
7001     
7002     findColumnIndex : function(dataIndex){
7003         for(var i = 0, len = this.config.length; i < len; i++){
7004             if(this.config[i].dataIndex == dataIndex){
7005                 return i;
7006             }
7007         }
7008         return -1;
7009     },
7010     
7011     
7012     moveColumn : function(oldIndex, newIndex){
7013         var c = this.config[oldIndex];
7014         this.config.splice(oldIndex, 1);
7015         this.config.splice(newIndex, 0, c);
7016         this.dataMap = null;
7017         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7018     },
7019
7020     isLocked : function(colIndex){
7021         return this.config[colIndex].locked === true;
7022     },
7023
7024     setLocked : function(colIndex, value, suppressEvent){
7025         if(this.isLocked(colIndex) == value){
7026             return;
7027         }
7028         this.config[colIndex].locked = value;
7029         if(!suppressEvent){
7030             this.fireEvent("columnlockchange", this, colIndex, value);
7031         }
7032     },
7033
7034     getTotalLockedWidth : function(){
7035         var totalWidth = 0;
7036         for(var i = 0; i < this.config.length; i++){
7037             if(this.isLocked(i) && !this.isHidden(i)){
7038                 this.totalWidth += this.getColumnWidth(i);
7039             }
7040         }
7041         return totalWidth;
7042     },
7043
7044     getLockedCount : function(){
7045         for(var i = 0, len = this.config.length; i < len; i++){
7046             if(!this.isLocked(i)){
7047                 return i;
7048             }
7049         }
7050         
7051         return this.config.length;
7052     },
7053
7054     /**
7055      * Returns the number of columns.
7056      * @return {Number}
7057      */
7058     getColumnCount : function(visibleOnly){
7059         if(visibleOnly === true){
7060             var c = 0;
7061             for(var i = 0, len = this.config.length; i < len; i++){
7062                 if(!this.isHidden(i)){
7063                     c++;
7064                 }
7065             }
7066             return c;
7067         }
7068         return this.config.length;
7069     },
7070
7071     /**
7072      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7073      * @param {Function} fn
7074      * @param {Object} scope (optional)
7075      * @return {Array} result
7076      */
7077     getColumnsBy : function(fn, scope){
7078         var r = [];
7079         for(var i = 0, len = this.config.length; i < len; i++){
7080             var c = this.config[i];
7081             if(fn.call(scope||this, c, i) === true){
7082                 r[r.length] = c;
7083             }
7084         }
7085         return r;
7086     },
7087
7088     /**
7089      * Returns true if the specified column is sortable.
7090      * @param {Number} col The column index
7091      * @return {Boolean}
7092      */
7093     isSortable : function(col){
7094         if(typeof this.config[col].sortable == "undefined"){
7095             return this.defaultSortable;
7096         }
7097         return this.config[col].sortable;
7098     },
7099
7100     /**
7101      * Returns the rendering (formatting) function defined for the column.
7102      * @param {Number} col The column index.
7103      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7104      */
7105     getRenderer : function(col){
7106         if(!this.config[col].renderer){
7107             return Roo.grid.ColumnModel.defaultRenderer;
7108         }
7109         return this.config[col].renderer;
7110     },
7111
7112     /**
7113      * Sets the rendering (formatting) function for a column.
7114      * @param {Number} col The column index
7115      * @param {Function} fn The function to use to process the cell's raw data
7116      * to return HTML markup for the grid view. The render function is called with
7117      * the following parameters:<ul>
7118      * <li>Data value.</li>
7119      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7120      * <li>css A CSS style string to apply to the table cell.</li>
7121      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7122      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7123      * <li>Row index</li>
7124      * <li>Column index</li>
7125      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7126      */
7127     setRenderer : function(col, fn){
7128         this.config[col].renderer = fn;
7129     },
7130
7131     /**
7132      * Returns the width for the specified column.
7133      * @param {Number} col The column index
7134      * @return {Number}
7135      */
7136     getColumnWidth : function(col){
7137         return this.config[col].width * 1 || this.defaultWidth;
7138     },
7139
7140     /**
7141      * Sets the width for a column.
7142      * @param {Number} col The column index
7143      * @param {Number} width The new width
7144      */
7145     setColumnWidth : function(col, width, suppressEvent){
7146         this.config[col].width = width;
7147         this.totalWidth = null;
7148         if(!suppressEvent){
7149              this.fireEvent("widthchange", this, col, width);
7150         }
7151     },
7152
7153     /**
7154      * Returns the total width of all columns.
7155      * @param {Boolean} includeHidden True to include hidden column widths
7156      * @return {Number}
7157      */
7158     getTotalWidth : function(includeHidden){
7159         if(!this.totalWidth){
7160             this.totalWidth = 0;
7161             for(var i = 0, len = this.config.length; i < len; i++){
7162                 if(includeHidden || !this.isHidden(i)){
7163                     this.totalWidth += this.getColumnWidth(i);
7164                 }
7165             }
7166         }
7167         return this.totalWidth;
7168     },
7169
7170     /**
7171      * Returns the header for the specified column.
7172      * @param {Number} col The column index
7173      * @return {String}
7174      */
7175     getColumnHeader : function(col){
7176         return this.config[col].header;
7177     },
7178
7179     /**
7180      * Sets the header for a column.
7181      * @param {Number} col The column index
7182      * @param {String} header The new header
7183      */
7184     setColumnHeader : function(col, header){
7185         this.config[col].header = header;
7186         this.fireEvent("headerchange", this, col, header);
7187     },
7188
7189     /**
7190      * Returns the tooltip for the specified column.
7191      * @param {Number} col The column index
7192      * @return {String}
7193      */
7194     getColumnTooltip : function(col){
7195             return this.config[col].tooltip;
7196     },
7197     /**
7198      * Sets the tooltip for a column.
7199      * @param {Number} col The column index
7200      * @param {String} tooltip The new tooltip
7201      */
7202     setColumnTooltip : function(col, tooltip){
7203             this.config[col].tooltip = tooltip;
7204     },
7205
7206     /**
7207      * Returns the dataIndex for the specified column.
7208      * @param {Number} col The column index
7209      * @return {Number}
7210      */
7211     getDataIndex : function(col){
7212         return this.config[col].dataIndex;
7213     },
7214
7215     /**
7216      * Sets the dataIndex for a column.
7217      * @param {Number} col The column index
7218      * @param {Number} dataIndex The new dataIndex
7219      */
7220     setDataIndex : function(col, dataIndex){
7221         this.config[col].dataIndex = dataIndex;
7222     },
7223
7224     
7225     
7226     /**
7227      * Returns true if the cell is editable.
7228      * @param {Number} colIndex The column index
7229      * @param {Number} rowIndex The row index - this is nto actually used..?
7230      * @return {Boolean}
7231      */
7232     isCellEditable : function(colIndex, rowIndex){
7233         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7234     },
7235
7236     /**
7237      * Returns the editor defined for the cell/column.
7238      * return false or null to disable editing.
7239      * @param {Number} colIndex The column index
7240      * @param {Number} rowIndex The row index
7241      * @return {Object}
7242      */
7243     getCellEditor : function(colIndex, rowIndex){
7244         return this.config[colIndex].editor;
7245     },
7246
7247     /**
7248      * Sets if a column is editable.
7249      * @param {Number} col The column index
7250      * @param {Boolean} editable True if the column is editable
7251      */
7252     setEditable : function(col, editable){
7253         this.config[col].editable = editable;
7254     },
7255
7256
7257     /**
7258      * Returns true if the column is hidden.
7259      * @param {Number} colIndex The column index
7260      * @return {Boolean}
7261      */
7262     isHidden : function(colIndex){
7263         return this.config[colIndex].hidden;
7264     },
7265
7266
7267     /**
7268      * Returns true if the column width cannot be changed
7269      */
7270     isFixed : function(colIndex){
7271         return this.config[colIndex].fixed;
7272     },
7273
7274     /**
7275      * Returns true if the column can be resized
7276      * @return {Boolean}
7277      */
7278     isResizable : function(colIndex){
7279         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7280     },
7281     /**
7282      * Sets if a column is hidden.
7283      * @param {Number} colIndex The column index
7284      * @param {Boolean} hidden True if the column is hidden
7285      */
7286     setHidden : function(colIndex, hidden){
7287         this.config[colIndex].hidden = hidden;
7288         this.totalWidth = null;
7289         this.fireEvent("hiddenchange", this, colIndex, hidden);
7290     },
7291
7292     /**
7293      * Sets the editor for a column.
7294      * @param {Number} col The column index
7295      * @param {Object} editor The editor object
7296      */
7297     setEditor : function(col, editor){
7298         this.config[col].editor = editor;
7299     }
7300 });
7301
7302 Roo.grid.ColumnModel.defaultRenderer = function(value)
7303 {
7304     if(typeof value == "object") {
7305         return value;
7306     }
7307         if(typeof value == "string" && value.length < 1){
7308             return "&#160;";
7309         }
7310     
7311         return String.format("{0}", value);
7312 };
7313
7314 // Alias for backwards compatibility
7315 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7316 /*
7317  * Based on:
7318  * Ext JS Library 1.1.1
7319  * Copyright(c) 2006-2007, Ext JS, LLC.
7320  *
7321  * Originally Released Under LGPL - original licence link has changed is not relivant.
7322  *
7323  * Fork - LGPL
7324  * <script type="text/javascript">
7325  */
7326  
7327 /**
7328  * @class Roo.LoadMask
7329  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7330  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7331  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7332  * element's UpdateManager load indicator and will be destroyed after the initial load.
7333  * @constructor
7334  * Create a new LoadMask
7335  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7336  * @param {Object} config The config object
7337  */
7338 Roo.LoadMask = function(el, config){
7339     this.el = Roo.get(el);
7340     Roo.apply(this, config);
7341     if(this.store){
7342         this.store.on('beforeload', this.onBeforeLoad, this);
7343         this.store.on('load', this.onLoad, this);
7344         this.store.on('loadexception', this.onLoadException, this);
7345         this.removeMask = false;
7346     }else{
7347         var um = this.el.getUpdateManager();
7348         um.showLoadIndicator = false; // disable the default indicator
7349         um.on('beforeupdate', this.onBeforeLoad, this);
7350         um.on('update', this.onLoad, this);
7351         um.on('failure', this.onLoad, this);
7352         this.removeMask = true;
7353     }
7354 };
7355
7356 Roo.LoadMask.prototype = {
7357     /**
7358      * @cfg {Boolean} removeMask
7359      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7360      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7361      */
7362     /**
7363      * @cfg {String} msg
7364      * The text to display in a centered loading message box (defaults to 'Loading...')
7365      */
7366     msg : 'Loading...',
7367     /**
7368      * @cfg {String} msgCls
7369      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7370      */
7371     msgCls : 'x-mask-loading',
7372
7373     /**
7374      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7375      * @type Boolean
7376      */
7377     disabled: false,
7378
7379     /**
7380      * Disables the mask to prevent it from being displayed
7381      */
7382     disable : function(){
7383        this.disabled = true;
7384     },
7385
7386     /**
7387      * Enables the mask so that it can be displayed
7388      */
7389     enable : function(){
7390         this.disabled = false;
7391     },
7392     
7393     onLoadException : function()
7394     {
7395         Roo.log(arguments);
7396         
7397         if (typeof(arguments[3]) != 'undefined') {
7398             Roo.MessageBox.alert("Error loading",arguments[3]);
7399         } 
7400         /*
7401         try {
7402             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7403                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7404             }   
7405         } catch(e) {
7406             
7407         }
7408         */
7409     
7410         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7411     },
7412     // private
7413     onLoad : function()
7414     {
7415         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7416     },
7417
7418     // private
7419     onBeforeLoad : function(){
7420         if(!this.disabled){
7421             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7422         }
7423     },
7424
7425     // private
7426     destroy : function(){
7427         if(this.store){
7428             this.store.un('beforeload', this.onBeforeLoad, this);
7429             this.store.un('load', this.onLoad, this);
7430             this.store.un('loadexception', this.onLoadException, this);
7431         }else{
7432             var um = this.el.getUpdateManager();
7433             um.un('beforeupdate', this.onBeforeLoad, this);
7434             um.un('update', this.onLoad, this);
7435             um.un('failure', this.onLoad, this);
7436         }
7437     }
7438 };/*
7439  * - LGPL
7440  *
7441  * table
7442  * 
7443  */
7444
7445 /**
7446  * @class Roo.bootstrap.Table
7447  * @extends Roo.bootstrap.Component
7448  * Bootstrap Table class
7449  * @cfg {String} cls table class
7450  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7451  * @cfg {String} bgcolor Specifies the background color for a table
7452  * @cfg {Number} border Specifies whether the table cells should have borders or not
7453  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7454  * @cfg {Number} cellspacing Specifies the space between cells
7455  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7456  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7457  * @cfg {String} sortable Specifies that the table should be sortable
7458  * @cfg {String} summary Specifies a summary of the content of a table
7459  * @cfg {Number} width Specifies the width of a table
7460  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7461  * 
7462  * @cfg {boolean} striped Should the rows be alternative striped
7463  * @cfg {boolean} bordered Add borders to the table
7464  * @cfg {boolean} hover Add hover highlighting
7465  * @cfg {boolean} condensed Format condensed
7466  * @cfg {boolean} responsive Format condensed
7467  * @cfg {Boolean} loadMask (true|false) default false
7468  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7469  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7470  * @cfg {Boolean} rowSelection (true|false) default false
7471  * @cfg {Boolean} cellSelection (true|false) default false
7472  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7473  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7474  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7475  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7476  
7477  * 
7478  * @constructor
7479  * Create a new Table
7480  * @param {Object} config The config object
7481  */
7482
7483 Roo.bootstrap.Table = function(config){
7484     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7485     
7486   
7487     
7488     // BC...
7489     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7490     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7491     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7492     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7493     
7494     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7495     if (this.sm) {
7496         this.sm.grid = this;
7497         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7498         this.sm = this.selModel;
7499         this.sm.xmodule = this.xmodule || false;
7500     }
7501     
7502     if (this.cm && typeof(this.cm.config) == 'undefined') {
7503         this.colModel = new Roo.grid.ColumnModel(this.cm);
7504         this.cm = this.colModel;
7505         this.cm.xmodule = this.xmodule || false;
7506     }
7507     if (this.store) {
7508         this.store= Roo.factory(this.store, Roo.data);
7509         this.ds = this.store;
7510         this.ds.xmodule = this.xmodule || false;
7511          
7512     }
7513     if (this.footer && this.store) {
7514         this.footer.dataSource = this.ds;
7515         this.footer = Roo.factory(this.footer);
7516     }
7517     
7518     /** @private */
7519     this.addEvents({
7520         /**
7521          * @event cellclick
7522          * Fires when a cell is clicked
7523          * @param {Roo.bootstrap.Table} this
7524          * @param {Roo.Element} el
7525          * @param {Number} rowIndex
7526          * @param {Number} columnIndex
7527          * @param {Roo.EventObject} e
7528          */
7529         "cellclick" : true,
7530         /**
7531          * @event celldblclick
7532          * Fires when a cell is double clicked
7533          * @param {Roo.bootstrap.Table} this
7534          * @param {Roo.Element} el
7535          * @param {Number} rowIndex
7536          * @param {Number} columnIndex
7537          * @param {Roo.EventObject} e
7538          */
7539         "celldblclick" : true,
7540         /**
7541          * @event rowclick
7542          * Fires when a row is clicked
7543          * @param {Roo.bootstrap.Table} this
7544          * @param {Roo.Element} el
7545          * @param {Number} rowIndex
7546          * @param {Roo.EventObject} e
7547          */
7548         "rowclick" : true,
7549         /**
7550          * @event rowdblclick
7551          * Fires when a row is double clicked
7552          * @param {Roo.bootstrap.Table} this
7553          * @param {Roo.Element} el
7554          * @param {Number} rowIndex
7555          * @param {Roo.EventObject} e
7556          */
7557         "rowdblclick" : true,
7558         /**
7559          * @event mouseover
7560          * Fires when a mouseover occur
7561          * @param {Roo.bootstrap.Table} this
7562          * @param {Roo.Element} el
7563          * @param {Number} rowIndex
7564          * @param {Number} columnIndex
7565          * @param {Roo.EventObject} e
7566          */
7567         "mouseover" : true,
7568         /**
7569          * @event mouseout
7570          * Fires when a mouseout occur
7571          * @param {Roo.bootstrap.Table} this
7572          * @param {Roo.Element} el
7573          * @param {Number} rowIndex
7574          * @param {Number} columnIndex
7575          * @param {Roo.EventObject} e
7576          */
7577         "mouseout" : true,
7578         /**
7579          * @event rowclass
7580          * Fires when a row is rendered, so you can change add a style to it.
7581          * @param {Roo.bootstrap.Table} this
7582          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7583          */
7584         'rowclass' : true,
7585           /**
7586          * @event rowsrendered
7587          * Fires when all the  rows have been rendered
7588          * @param {Roo.bootstrap.Table} this
7589          */
7590         'rowsrendered' : true,
7591         /**
7592          * @event contextmenu
7593          * The raw contextmenu event for the entire grid.
7594          * @param {Roo.EventObject} e
7595          */
7596         "contextmenu" : true,
7597         /**
7598          * @event rowcontextmenu
7599          * Fires when a row is right clicked
7600          * @param {Roo.bootstrap.Table} this
7601          * @param {Number} rowIndex
7602          * @param {Roo.EventObject} e
7603          */
7604         "rowcontextmenu" : true,
7605         /**
7606          * @event cellcontextmenu
7607          * Fires when a cell is right clicked
7608          * @param {Roo.bootstrap.Table} this
7609          * @param {Number} rowIndex
7610          * @param {Number} cellIndex
7611          * @param {Roo.EventObject} e
7612          */
7613          "cellcontextmenu" : true,
7614          /**
7615          * @event headercontextmenu
7616          * Fires when a header is right clicked
7617          * @param {Roo.bootstrap.Table} this
7618          * @param {Number} columnIndex
7619          * @param {Roo.EventObject} e
7620          */
7621         "headercontextmenu" : true
7622     });
7623 };
7624
7625 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7626     
7627     cls: false,
7628     align: false,
7629     bgcolor: false,
7630     border: false,
7631     cellpadding: false,
7632     cellspacing: false,
7633     frame: false,
7634     rules: false,
7635     sortable: false,
7636     summary: false,
7637     width: false,
7638     striped : false,
7639     scrollBody : false,
7640     bordered: false,
7641     hover:  false,
7642     condensed : false,
7643     responsive : false,
7644     sm : false,
7645     cm : false,
7646     store : false,
7647     loadMask : false,
7648     footerShow : true,
7649     headerShow : true,
7650   
7651     rowSelection : false,
7652     cellSelection : false,
7653     layout : false,
7654     
7655     // Roo.Element - the tbody
7656     mainBody: false,
7657     // Roo.Element - thead element
7658     mainHead: false,
7659     
7660     container: false, // used by gridpanel...
7661     
7662     lazyLoad : false,
7663     
7664     CSS : Roo.util.CSS,
7665     
7666     auto_hide_footer : false,
7667     
7668     getAutoCreate : function()
7669     {
7670         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7671         
7672         cfg = {
7673             tag: 'table',
7674             cls : 'table',
7675             cn : []
7676         };
7677         if (this.scrollBody) {
7678             cfg.cls += ' table-body-fixed';
7679         }    
7680         if (this.striped) {
7681             cfg.cls += ' table-striped';
7682         }
7683         
7684         if (this.hover) {
7685             cfg.cls += ' table-hover';
7686         }
7687         if (this.bordered) {
7688             cfg.cls += ' table-bordered';
7689         }
7690         if (this.condensed) {
7691             cfg.cls += ' table-condensed';
7692         }
7693         if (this.responsive) {
7694             cfg.cls += ' table-responsive';
7695         }
7696         
7697         if (this.cls) {
7698             cfg.cls+=  ' ' +this.cls;
7699         }
7700         
7701         // this lot should be simplifed...
7702         var _t = this;
7703         var cp = [
7704             'align',
7705             'bgcolor',
7706             'border',
7707             'cellpadding',
7708             'cellspacing',
7709             'frame',
7710             'rules',
7711             'sortable',
7712             'summary',
7713             'width'
7714         ].forEach(function(k) {
7715             if (_t[k]) {
7716                 cfg[k] = _t[k];
7717             }
7718         });
7719         
7720         
7721         if (this.layout) {
7722             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7723         }
7724         
7725         if(this.store || this.cm){
7726             if(this.headerShow){
7727                 cfg.cn.push(this.renderHeader());
7728             }
7729             
7730             cfg.cn.push(this.renderBody());
7731             
7732             if(this.footerShow){
7733                 cfg.cn.push(this.renderFooter());
7734             }
7735             // where does this come from?
7736             //cfg.cls+=  ' TableGrid';
7737         }
7738         
7739         return { cn : [ cfg ] };
7740     },
7741     
7742     initEvents : function()
7743     {   
7744         if(!this.store || !this.cm){
7745             return;
7746         }
7747         if (this.selModel) {
7748             this.selModel.initEvents();
7749         }
7750         
7751         
7752         //Roo.log('initEvents with ds!!!!');
7753         
7754         this.mainBody = this.el.select('tbody', true).first();
7755         this.mainHead = this.el.select('thead', true).first();
7756         this.mainFoot = this.el.select('tfoot', true).first();
7757         
7758         
7759         
7760         var _this = this;
7761         
7762         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7763             e.on('click', _this.sort, _this);
7764         });
7765         
7766         this.mainBody.on("click", this.onClick, this);
7767         this.mainBody.on("dblclick", this.onDblClick, this);
7768         
7769         // why is this done????? = it breaks dialogs??
7770         //this.parent().el.setStyle('position', 'relative');
7771         
7772         
7773         if (this.footer) {
7774             this.footer.parentId = this.id;
7775             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7776             
7777             if(this.lazyLoad){
7778                 this.el.select('tfoot tr td').first().addClass('hide');
7779             }
7780         } 
7781         
7782         if(this.loadMask) {
7783             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7784         }
7785         
7786         this.store.on('load', this.onLoad, this);
7787         this.store.on('beforeload', this.onBeforeLoad, this);
7788         this.store.on('update', this.onUpdate, this);
7789         this.store.on('add', this.onAdd, this);
7790         this.store.on("clear", this.clear, this);
7791         
7792         this.el.on("contextmenu", this.onContextMenu, this);
7793         
7794         this.mainBody.on('scroll', this.onBodyScroll, this);
7795         
7796         this.cm.on("headerchange", this.onHeaderChange, this);
7797         
7798         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7799         
7800     },
7801     
7802     onContextMenu : function(e, t)
7803     {
7804         this.processEvent("contextmenu", e);
7805     },
7806     
7807     processEvent : function(name, e)
7808     {
7809         if (name != 'touchstart' ) {
7810             this.fireEvent(name, e);    
7811         }
7812         
7813         var t = e.getTarget();
7814         
7815         var cell = Roo.get(t);
7816         
7817         if(!cell){
7818             return;
7819         }
7820         
7821         if(cell.findParent('tfoot', false, true)){
7822             return;
7823         }
7824         
7825         if(cell.findParent('thead', false, true)){
7826             
7827             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7828                 cell = Roo.get(t).findParent('th', false, true);
7829                 if (!cell) {
7830                     Roo.log("failed to find th in thead?");
7831                     Roo.log(e.getTarget());
7832                     return;
7833                 }
7834             }
7835             
7836             var cellIndex = cell.dom.cellIndex;
7837             
7838             var ename = name == 'touchstart' ? 'click' : name;
7839             this.fireEvent("header" + ename, this, cellIndex, e);
7840             
7841             return;
7842         }
7843         
7844         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7845             cell = Roo.get(t).findParent('td', false, true);
7846             if (!cell) {
7847                 Roo.log("failed to find th in tbody?");
7848                 Roo.log(e.getTarget());
7849                 return;
7850             }
7851         }
7852         
7853         var row = cell.findParent('tr', false, true);
7854         var cellIndex = cell.dom.cellIndex;
7855         var rowIndex = row.dom.rowIndex - 1;
7856         
7857         if(row !== false){
7858             
7859             this.fireEvent("row" + name, this, rowIndex, e);
7860             
7861             if(cell !== false){
7862             
7863                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7864             }
7865         }
7866         
7867     },
7868     
7869     onMouseover : function(e, el)
7870     {
7871         var cell = Roo.get(el);
7872         
7873         if(!cell){
7874             return;
7875         }
7876         
7877         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7878             cell = cell.findParent('td', false, true);
7879         }
7880         
7881         var row = cell.findParent('tr', false, true);
7882         var cellIndex = cell.dom.cellIndex;
7883         var rowIndex = row.dom.rowIndex - 1; // start from 0
7884         
7885         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7886         
7887     },
7888     
7889     onMouseout : function(e, el)
7890     {
7891         var cell = Roo.get(el);
7892         
7893         if(!cell){
7894             return;
7895         }
7896         
7897         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7898             cell = cell.findParent('td', false, true);
7899         }
7900         
7901         var row = cell.findParent('tr', false, true);
7902         var cellIndex = cell.dom.cellIndex;
7903         var rowIndex = row.dom.rowIndex - 1; // start from 0
7904         
7905         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7906         
7907     },
7908     
7909     onClick : function(e, el)
7910     {
7911         var cell = Roo.get(el);
7912         
7913         if(!cell || (!this.cellSelection && !this.rowSelection)){
7914             return;
7915         }
7916         
7917         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7918             cell = cell.findParent('td', false, true);
7919         }
7920         
7921         if(!cell || typeof(cell) == 'undefined'){
7922             return;
7923         }
7924         
7925         var row = cell.findParent('tr', false, true);
7926         
7927         if(!row || typeof(row) == 'undefined'){
7928             return;
7929         }
7930         
7931         var cellIndex = cell.dom.cellIndex;
7932         var rowIndex = this.getRowIndex(row);
7933         
7934         // why??? - should these not be based on SelectionModel?
7935         if(this.cellSelection){
7936             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7937         }
7938         
7939         if(this.rowSelection){
7940             this.fireEvent('rowclick', this, row, rowIndex, e);
7941         }
7942         
7943         
7944     },
7945         
7946     onDblClick : function(e,el)
7947     {
7948         var cell = Roo.get(el);
7949         
7950         if(!cell || (!this.cellSelection && !this.rowSelection)){
7951             return;
7952         }
7953         
7954         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7955             cell = cell.findParent('td', false, true);
7956         }
7957         
7958         if(!cell || typeof(cell) == 'undefined'){
7959             return;
7960         }
7961         
7962         var row = cell.findParent('tr', false, true);
7963         
7964         if(!row || typeof(row) == 'undefined'){
7965             return;
7966         }
7967         
7968         var cellIndex = cell.dom.cellIndex;
7969         var rowIndex = this.getRowIndex(row);
7970         
7971         if(this.cellSelection){
7972             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7973         }
7974         
7975         if(this.rowSelection){
7976             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7977         }
7978     },
7979     
7980     sort : function(e,el)
7981     {
7982         var col = Roo.get(el);
7983         
7984         if(!col.hasClass('sortable')){
7985             return;
7986         }
7987         
7988         var sort = col.attr('sort');
7989         var dir = 'ASC';
7990         
7991         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7992             dir = 'DESC';
7993         }
7994         
7995         this.store.sortInfo = {field : sort, direction : dir};
7996         
7997         if (this.footer) {
7998             Roo.log("calling footer first");
7999             this.footer.onClick('first');
8000         } else {
8001         
8002             this.store.load({ params : { start : 0 } });
8003         }
8004     },
8005     
8006     renderHeader : function()
8007     {
8008         var header = {
8009             tag: 'thead',
8010             cn : []
8011         };
8012         
8013         var cm = this.cm;
8014         this.totalWidth = 0;
8015         
8016         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8017             
8018             var config = cm.config[i];
8019             
8020             var c = {
8021                 tag: 'th',
8022                 cls : 'x-hcol-' + i,
8023                 style : '',
8024                 html: cm.getColumnHeader(i)
8025             };
8026             
8027             var hh = '';
8028             
8029             if(typeof(config.sortable) != 'undefined' && config.sortable){
8030                 c.cls = 'sortable';
8031                 c.html = '<i class="glyphicon"></i>' + c.html;
8032             }
8033             
8034             // could use BS4 hidden-..-down 
8035             
8036             if(typeof(config.lgHeader) != 'undefined'){
8037                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8038             }
8039             
8040             if(typeof(config.mdHeader) != 'undefined'){
8041                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8042             }
8043             
8044             if(typeof(config.smHeader) != 'undefined'){
8045                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8046             }
8047             
8048             if(typeof(config.xsHeader) != 'undefined'){
8049                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8050             }
8051             
8052             if(hh.length){
8053                 c.html = hh;
8054             }
8055             
8056             if(typeof(config.tooltip) != 'undefined'){
8057                 c.tooltip = config.tooltip;
8058             }
8059             
8060             if(typeof(config.colspan) != 'undefined'){
8061                 c.colspan = config.colspan;
8062             }
8063             
8064             if(typeof(config.hidden) != 'undefined' && config.hidden){
8065                 c.style += ' display:none;';
8066             }
8067             
8068             if(typeof(config.dataIndex) != 'undefined'){
8069                 c.sort = config.dataIndex;
8070             }
8071             
8072            
8073             
8074             if(typeof(config.align) != 'undefined' && config.align.length){
8075                 c.style += ' text-align:' + config.align + ';';
8076             }
8077             
8078             if(typeof(config.width) != 'undefined'){
8079                 c.style += ' width:' + config.width + 'px;';
8080                 this.totalWidth += config.width;
8081             } else {
8082                 this.totalWidth += 100; // assume minimum of 100 per column?
8083             }
8084             
8085             if(typeof(config.cls) != 'undefined'){
8086                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8087             }
8088             
8089             ['xs','sm','md','lg'].map(function(size){
8090                 
8091                 if(typeof(config[size]) == 'undefined'){
8092                     return;
8093                 }
8094                  
8095                 if (!config[size]) { // 0 = hidden
8096                     // BS 4 '0' is treated as hide that column and below.
8097                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8098                     return;
8099                 }
8100                 
8101                 c.cls += ' col-' + size + '-' + config[size] + (
8102                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8103                 );
8104                 
8105                 
8106             });
8107             
8108             header.cn.push(c)
8109         }
8110         
8111         return header;
8112     },
8113     
8114     renderBody : function()
8115     {
8116         var body = {
8117             tag: 'tbody',
8118             cn : [
8119                 {
8120                     tag: 'tr',
8121                     cn : [
8122                         {
8123                             tag : 'td',
8124                             colspan :  this.cm.getColumnCount()
8125                         }
8126                     ]
8127                 }
8128             ]
8129         };
8130         
8131         return body;
8132     },
8133     
8134     renderFooter : function()
8135     {
8136         var footer = {
8137             tag: 'tfoot',
8138             cn : [
8139                 {
8140                     tag: 'tr',
8141                     cn : [
8142                         {
8143                             tag : 'td',
8144                             colspan :  this.cm.getColumnCount()
8145                         }
8146                     ]
8147                 }
8148             ]
8149         };
8150         
8151         return footer;
8152     },
8153     
8154     
8155     
8156     onLoad : function()
8157     {
8158 //        Roo.log('ds onload');
8159         this.clear();
8160         
8161         var _this = this;
8162         var cm = this.cm;
8163         var ds = this.store;
8164         
8165         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8166             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8167             if (_this.store.sortInfo) {
8168                     
8169                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8170                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8171                 }
8172                 
8173                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8174                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8175                 }
8176             }
8177         });
8178         
8179         var tbody =  this.mainBody;
8180               
8181         if(ds.getCount() > 0){
8182             ds.data.each(function(d,rowIndex){
8183                 var row =  this.renderRow(cm, ds, rowIndex);
8184                 
8185                 tbody.createChild(row);
8186                 
8187                 var _this = this;
8188                 
8189                 if(row.cellObjects.length){
8190                     Roo.each(row.cellObjects, function(r){
8191                         _this.renderCellObject(r);
8192                     })
8193                 }
8194                 
8195             }, this);
8196         }
8197         
8198         var tfoot = this.el.select('tfoot', true).first();
8199         
8200         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8201             
8202             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8203             
8204             var total = this.ds.getTotalCount();
8205             
8206             if(this.footer.pageSize < total){
8207                 this.mainFoot.show();
8208             }
8209         }
8210         
8211         Roo.each(this.el.select('tbody td', true).elements, function(e){
8212             e.on('mouseover', _this.onMouseover, _this);
8213         });
8214         
8215         Roo.each(this.el.select('tbody td', true).elements, function(e){
8216             e.on('mouseout', _this.onMouseout, _this);
8217         });
8218         this.fireEvent('rowsrendered', this);
8219         
8220         this.autoSize();
8221     },
8222     
8223     
8224     onUpdate : function(ds,record)
8225     {
8226         this.refreshRow(record);
8227         this.autoSize();
8228     },
8229     
8230     onRemove : function(ds, record, index, isUpdate){
8231         if(isUpdate !== true){
8232             this.fireEvent("beforerowremoved", this, index, record);
8233         }
8234         var bt = this.mainBody.dom;
8235         
8236         var rows = this.el.select('tbody > tr', true).elements;
8237         
8238         if(typeof(rows[index]) != 'undefined'){
8239             bt.removeChild(rows[index].dom);
8240         }
8241         
8242 //        if(bt.rows[index]){
8243 //            bt.removeChild(bt.rows[index]);
8244 //        }
8245         
8246         if(isUpdate !== true){
8247             //this.stripeRows(index);
8248             //this.syncRowHeights(index, index);
8249             //this.layout();
8250             this.fireEvent("rowremoved", this, index, record);
8251         }
8252     },
8253     
8254     onAdd : function(ds, records, rowIndex)
8255     {
8256         //Roo.log('on Add called');
8257         // - note this does not handle multiple adding very well..
8258         var bt = this.mainBody.dom;
8259         for (var i =0 ; i < records.length;i++) {
8260             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8261             //Roo.log(records[i]);
8262             //Roo.log(this.store.getAt(rowIndex+i));
8263             this.insertRow(this.store, rowIndex + i, false);
8264             return;
8265         }
8266         
8267     },
8268     
8269     
8270     refreshRow : function(record){
8271         var ds = this.store, index;
8272         if(typeof record == 'number'){
8273             index = record;
8274             record = ds.getAt(index);
8275         }else{
8276             index = ds.indexOf(record);
8277             if (index < 0) {
8278                 return; // should not happen - but seems to 
8279             }
8280         }
8281         this.insertRow(ds, index, true);
8282         this.autoSize();
8283         this.onRemove(ds, record, index+1, true);
8284         this.autoSize();
8285         //this.syncRowHeights(index, index);
8286         //this.layout();
8287         this.fireEvent("rowupdated", this, index, record);
8288     },
8289     
8290     insertRow : function(dm, rowIndex, isUpdate){
8291         
8292         if(!isUpdate){
8293             this.fireEvent("beforerowsinserted", this, rowIndex);
8294         }
8295             //var s = this.getScrollState();
8296         var row = this.renderRow(this.cm, this.store, rowIndex);
8297         // insert before rowIndex..
8298         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8299         
8300         var _this = this;
8301                 
8302         if(row.cellObjects.length){
8303             Roo.each(row.cellObjects, function(r){
8304                 _this.renderCellObject(r);
8305             })
8306         }
8307             
8308         if(!isUpdate){
8309             this.fireEvent("rowsinserted", this, rowIndex);
8310             //this.syncRowHeights(firstRow, lastRow);
8311             //this.stripeRows(firstRow);
8312             //this.layout();
8313         }
8314         
8315     },
8316     
8317     
8318     getRowDom : function(rowIndex)
8319     {
8320         var rows = this.el.select('tbody > tr', true).elements;
8321         
8322         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8323         
8324     },
8325     // returns the object tree for a tr..
8326   
8327     
8328     renderRow : function(cm, ds, rowIndex) 
8329     {
8330         var d = ds.getAt(rowIndex);
8331         
8332         var row = {
8333             tag : 'tr',
8334             cls : 'x-row-' + rowIndex,
8335             cn : []
8336         };
8337             
8338         var cellObjects = [];
8339         
8340         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8341             var config = cm.config[i];
8342             
8343             var renderer = cm.getRenderer(i);
8344             var value = '';
8345             var id = false;
8346             
8347             if(typeof(renderer) !== 'undefined'){
8348                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8349             }
8350             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8351             // and are rendered into the cells after the row is rendered - using the id for the element.
8352             
8353             if(typeof(value) === 'object'){
8354                 id = Roo.id();
8355                 cellObjects.push({
8356                     container : id,
8357                     cfg : value 
8358                 })
8359             }
8360             
8361             var rowcfg = {
8362                 record: d,
8363                 rowIndex : rowIndex,
8364                 colIndex : i,
8365                 rowClass : ''
8366             };
8367
8368             this.fireEvent('rowclass', this, rowcfg);
8369             
8370             var td = {
8371                 tag: 'td',
8372                 cls : rowcfg.rowClass + ' x-col-' + i,
8373                 style: '',
8374                 html: (typeof(value) === 'object') ? '' : value
8375             };
8376             
8377             if (id) {
8378                 td.id = id;
8379             }
8380             
8381             if(typeof(config.colspan) != 'undefined'){
8382                 td.colspan = config.colspan;
8383             }
8384             
8385             if(typeof(config.hidden) != 'undefined' && config.hidden){
8386                 td.style += ' display:none;';
8387             }
8388             
8389             if(typeof(config.align) != 'undefined' && config.align.length){
8390                 td.style += ' text-align:' + config.align + ';';
8391             }
8392             if(typeof(config.valign) != 'undefined' && config.valign.length){
8393                 td.style += ' vertical-align:' + config.valign + ';';
8394             }
8395             
8396             if(typeof(config.width) != 'undefined'){
8397                 td.style += ' width:' +  config.width + 'px;';
8398             }
8399             
8400             if(typeof(config.cursor) != 'undefined'){
8401                 td.style += ' cursor:' +  config.cursor + ';';
8402             }
8403             
8404             if(typeof(config.cls) != 'undefined'){
8405                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8406             }
8407             
8408             ['xs','sm','md','lg'].map(function(size){
8409                 
8410                 if(typeof(config[size]) == 'undefined'){
8411                     return;
8412                 }
8413                 
8414                 
8415                   
8416                 if (!config[size]) { // 0 = hidden
8417                     // BS 4 '0' is treated as hide that column and below.
8418                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8419                     return;
8420                 }
8421                 
8422                 td.cls += ' col-' + size + '-' + config[size] + (
8423                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8424                 );
8425                  
8426
8427             });
8428             
8429             row.cn.push(td);
8430            
8431         }
8432         
8433         row.cellObjects = cellObjects;
8434         
8435         return row;
8436           
8437     },
8438     
8439     
8440     
8441     onBeforeLoad : function()
8442     {
8443         
8444     },
8445      /**
8446      * Remove all rows
8447      */
8448     clear : function()
8449     {
8450         this.el.select('tbody', true).first().dom.innerHTML = '';
8451     },
8452     /**
8453      * Show or hide a row.
8454      * @param {Number} rowIndex to show or hide
8455      * @param {Boolean} state hide
8456      */
8457     setRowVisibility : function(rowIndex, state)
8458     {
8459         var bt = this.mainBody.dom;
8460         
8461         var rows = this.el.select('tbody > tr', true).elements;
8462         
8463         if(typeof(rows[rowIndex]) == 'undefined'){
8464             return;
8465         }
8466         rows[rowIndex].dom.style.display = state ? '' : 'none';
8467     },
8468     
8469     
8470     getSelectionModel : function(){
8471         if(!this.selModel){
8472             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8473         }
8474         return this.selModel;
8475     },
8476     /*
8477      * Render the Roo.bootstrap object from renderder
8478      */
8479     renderCellObject : function(r)
8480     {
8481         var _this = this;
8482         
8483         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8484         
8485         var t = r.cfg.render(r.container);
8486         
8487         if(r.cfg.cn){
8488             Roo.each(r.cfg.cn, function(c){
8489                 var child = {
8490                     container: t.getChildContainer(),
8491                     cfg: c
8492                 };
8493                 _this.renderCellObject(child);
8494             })
8495         }
8496     },
8497     
8498     getRowIndex : function(row)
8499     {
8500         var rowIndex = -1;
8501         
8502         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8503             if(el != row){
8504                 return;
8505             }
8506             
8507             rowIndex = index;
8508         });
8509         
8510         return rowIndex;
8511     },
8512      /**
8513      * Returns the grid's underlying element = used by panel.Grid
8514      * @return {Element} The element
8515      */
8516     getGridEl : function(){
8517         return this.el;
8518     },
8519      /**
8520      * Forces a resize - used by panel.Grid
8521      * @return {Element} The element
8522      */
8523     autoSize : function()
8524     {
8525         //var ctr = Roo.get(this.container.dom.parentElement);
8526         var ctr = Roo.get(this.el.dom);
8527         
8528         var thd = this.getGridEl().select('thead',true).first();
8529         var tbd = this.getGridEl().select('tbody', true).first();
8530         var tfd = this.getGridEl().select('tfoot', true).first();
8531         
8532         var cw = ctr.getWidth();
8533         
8534         if (tbd) {
8535             
8536             tbd.setWidth(ctr.getWidth());
8537             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8538             // this needs fixing for various usage - currently only hydra job advers I think..
8539             //tdb.setHeight(
8540             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8541             //); 
8542             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8543             cw -= barsize;
8544         }
8545         cw = Math.max(cw, this.totalWidth);
8546         this.getGridEl().select('tr',true).setWidth(cw);
8547         // resize 'expandable coloumn?
8548         
8549         return; // we doe not have a view in this design..
8550         
8551     },
8552     onBodyScroll: function()
8553     {
8554         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8555         if(this.mainHead){
8556             this.mainHead.setStyle({
8557                 'position' : 'relative',
8558                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8559             });
8560         }
8561         
8562         if(this.lazyLoad){
8563             
8564             var scrollHeight = this.mainBody.dom.scrollHeight;
8565             
8566             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8567             
8568             var height = this.mainBody.getHeight();
8569             
8570             if(scrollHeight - height == scrollTop) {
8571                 
8572                 var total = this.ds.getTotalCount();
8573                 
8574                 if(this.footer.cursor + this.footer.pageSize < total){
8575                     
8576                     this.footer.ds.load({
8577                         params : {
8578                             start : this.footer.cursor + this.footer.pageSize,
8579                             limit : this.footer.pageSize
8580                         },
8581                         add : true
8582                     });
8583                 }
8584             }
8585             
8586         }
8587     },
8588     
8589     onHeaderChange : function()
8590     {
8591         var header = this.renderHeader();
8592         var table = this.el.select('table', true).first();
8593         
8594         this.mainHead.remove();
8595         this.mainHead = table.createChild(header, this.mainBody, false);
8596     },
8597     
8598     onHiddenChange : function(colModel, colIndex, hidden)
8599     {
8600         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8601         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8602         
8603         this.CSS.updateRule(thSelector, "display", "");
8604         this.CSS.updateRule(tdSelector, "display", "");
8605         
8606         if(hidden){
8607             this.CSS.updateRule(thSelector, "display", "none");
8608             this.CSS.updateRule(tdSelector, "display", "none");
8609         }
8610         
8611         this.onHeaderChange();
8612         this.onLoad();
8613     },
8614     
8615     setColumnWidth: function(col_index, width)
8616     {
8617         // width = "md-2 xs-2..."
8618         if(!this.colModel.config[col_index]) {
8619             return;
8620         }
8621         
8622         var w = width.split(" ");
8623         
8624         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8625         
8626         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8627         
8628         
8629         for(var j = 0; j < w.length; j++) {
8630             
8631             if(!w[j]) {
8632                 continue;
8633             }
8634             
8635             var size_cls = w[j].split("-");
8636             
8637             if(!Number.isInteger(size_cls[1] * 1)) {
8638                 continue;
8639             }
8640             
8641             if(!this.colModel.config[col_index][size_cls[0]]) {
8642                 continue;
8643             }
8644             
8645             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8646                 continue;
8647             }
8648             
8649             h_row[0].classList.replace(
8650                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8651                 "col-"+size_cls[0]+"-"+size_cls[1]
8652             );
8653             
8654             for(var i = 0; i < rows.length; i++) {
8655                 
8656                 var size_cls = w[j].split("-");
8657                 
8658                 if(!Number.isInteger(size_cls[1] * 1)) {
8659                     continue;
8660                 }
8661                 
8662                 if(!this.colModel.config[col_index][size_cls[0]]) {
8663                     continue;
8664                 }
8665                 
8666                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8667                     continue;
8668                 }
8669                 
8670                 rows[i].classList.replace(
8671                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8672                     "col-"+size_cls[0]+"-"+size_cls[1]
8673                 );
8674             }
8675             
8676             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8677         }
8678     }
8679 });
8680
8681  
8682
8683  /*
8684  * - LGPL
8685  *
8686  * table cell
8687  * 
8688  */
8689
8690 /**
8691  * @class Roo.bootstrap.TableCell
8692  * @extends Roo.bootstrap.Component
8693  * Bootstrap TableCell class
8694  * @cfg {String} html cell contain text
8695  * @cfg {String} cls cell class
8696  * @cfg {String} tag cell tag (td|th) default td
8697  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8698  * @cfg {String} align Aligns the content in a cell
8699  * @cfg {String} axis Categorizes cells
8700  * @cfg {String} bgcolor Specifies the background color of a cell
8701  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8702  * @cfg {Number} colspan Specifies the number of columns a cell should span
8703  * @cfg {String} headers Specifies one or more header cells a cell is related to
8704  * @cfg {Number} height Sets the height of a cell
8705  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8706  * @cfg {Number} rowspan Sets the number of rows a cell should span
8707  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8708  * @cfg {String} valign Vertical aligns the content in a cell
8709  * @cfg {Number} width Specifies the width of a cell
8710  * 
8711  * @constructor
8712  * Create a new TableCell
8713  * @param {Object} config The config object
8714  */
8715
8716 Roo.bootstrap.TableCell = function(config){
8717     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8718 };
8719
8720 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8721     
8722     html: false,
8723     cls: false,
8724     tag: false,
8725     abbr: false,
8726     align: false,
8727     axis: false,
8728     bgcolor: false,
8729     charoff: false,
8730     colspan: false,
8731     headers: false,
8732     height: false,
8733     nowrap: false,
8734     rowspan: false,
8735     scope: false,
8736     valign: false,
8737     width: false,
8738     
8739     
8740     getAutoCreate : function(){
8741         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8742         
8743         cfg = {
8744             tag: 'td'
8745         };
8746         
8747         if(this.tag){
8748             cfg.tag = this.tag;
8749         }
8750         
8751         if (this.html) {
8752             cfg.html=this.html
8753         }
8754         if (this.cls) {
8755             cfg.cls=this.cls
8756         }
8757         if (this.abbr) {
8758             cfg.abbr=this.abbr
8759         }
8760         if (this.align) {
8761             cfg.align=this.align
8762         }
8763         if (this.axis) {
8764             cfg.axis=this.axis
8765         }
8766         if (this.bgcolor) {
8767             cfg.bgcolor=this.bgcolor
8768         }
8769         if (this.charoff) {
8770             cfg.charoff=this.charoff
8771         }
8772         if (this.colspan) {
8773             cfg.colspan=this.colspan
8774         }
8775         if (this.headers) {
8776             cfg.headers=this.headers
8777         }
8778         if (this.height) {
8779             cfg.height=this.height
8780         }
8781         if (this.nowrap) {
8782             cfg.nowrap=this.nowrap
8783         }
8784         if (this.rowspan) {
8785             cfg.rowspan=this.rowspan
8786         }
8787         if (this.scope) {
8788             cfg.scope=this.scope
8789         }
8790         if (this.valign) {
8791             cfg.valign=this.valign
8792         }
8793         if (this.width) {
8794             cfg.width=this.width
8795         }
8796         
8797         
8798         return cfg;
8799     }
8800    
8801 });
8802
8803  
8804
8805  /*
8806  * - LGPL
8807  *
8808  * table row
8809  * 
8810  */
8811
8812 /**
8813  * @class Roo.bootstrap.TableRow
8814  * @extends Roo.bootstrap.Component
8815  * Bootstrap TableRow class
8816  * @cfg {String} cls row class
8817  * @cfg {String} align Aligns the content in a table row
8818  * @cfg {String} bgcolor Specifies a background color for a table row
8819  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8820  * @cfg {String} valign Vertical aligns the content in a table row
8821  * 
8822  * @constructor
8823  * Create a new TableRow
8824  * @param {Object} config The config object
8825  */
8826
8827 Roo.bootstrap.TableRow = function(config){
8828     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8829 };
8830
8831 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8832     
8833     cls: false,
8834     align: false,
8835     bgcolor: false,
8836     charoff: false,
8837     valign: false,
8838     
8839     getAutoCreate : function(){
8840         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8841         
8842         cfg = {
8843             tag: 'tr'
8844         };
8845             
8846         if(this.cls){
8847             cfg.cls = this.cls;
8848         }
8849         if(this.align){
8850             cfg.align = this.align;
8851         }
8852         if(this.bgcolor){
8853             cfg.bgcolor = this.bgcolor;
8854         }
8855         if(this.charoff){
8856             cfg.charoff = this.charoff;
8857         }
8858         if(this.valign){
8859             cfg.valign = this.valign;
8860         }
8861         
8862         return cfg;
8863     }
8864    
8865 });
8866
8867  
8868
8869  /*
8870  * - LGPL
8871  *
8872  * table body
8873  * 
8874  */
8875
8876 /**
8877  * @class Roo.bootstrap.TableBody
8878  * @extends Roo.bootstrap.Component
8879  * Bootstrap TableBody class
8880  * @cfg {String} cls element class
8881  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8882  * @cfg {String} align Aligns the content inside the element
8883  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8884  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8885  * 
8886  * @constructor
8887  * Create a new TableBody
8888  * @param {Object} config The config object
8889  */
8890
8891 Roo.bootstrap.TableBody = function(config){
8892     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8893 };
8894
8895 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8896     
8897     cls: false,
8898     tag: false,
8899     align: false,
8900     charoff: false,
8901     valign: false,
8902     
8903     getAutoCreate : function(){
8904         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8905         
8906         cfg = {
8907             tag: 'tbody'
8908         };
8909             
8910         if (this.cls) {
8911             cfg.cls=this.cls
8912         }
8913         if(this.tag){
8914             cfg.tag = this.tag;
8915         }
8916         
8917         if(this.align){
8918             cfg.align = this.align;
8919         }
8920         if(this.charoff){
8921             cfg.charoff = this.charoff;
8922         }
8923         if(this.valign){
8924             cfg.valign = this.valign;
8925         }
8926         
8927         return cfg;
8928     }
8929     
8930     
8931 //    initEvents : function()
8932 //    {
8933 //        
8934 //        if(!this.store){
8935 //            return;
8936 //        }
8937 //        
8938 //        this.store = Roo.factory(this.store, Roo.data);
8939 //        this.store.on('load', this.onLoad, this);
8940 //        
8941 //        this.store.load();
8942 //        
8943 //    },
8944 //    
8945 //    onLoad: function () 
8946 //    {   
8947 //        this.fireEvent('load', this);
8948 //    }
8949 //    
8950 //   
8951 });
8952
8953  
8954
8955  /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965
8966 // as we use this in bootstrap.
8967 Roo.namespace('Roo.form');
8968  /**
8969  * @class Roo.form.Action
8970  * Internal Class used to handle form actions
8971  * @constructor
8972  * @param {Roo.form.BasicForm} el The form element or its id
8973  * @param {Object} config Configuration options
8974  */
8975
8976  
8977  
8978 // define the action interface
8979 Roo.form.Action = function(form, options){
8980     this.form = form;
8981     this.options = options || {};
8982 };
8983 /**
8984  * Client Validation Failed
8985  * @const 
8986  */
8987 Roo.form.Action.CLIENT_INVALID = 'client';
8988 /**
8989  * Server Validation Failed
8990  * @const 
8991  */
8992 Roo.form.Action.SERVER_INVALID = 'server';
8993  /**
8994  * Connect to Server Failed
8995  * @const 
8996  */
8997 Roo.form.Action.CONNECT_FAILURE = 'connect';
8998 /**
8999  * Reading Data from Server Failed
9000  * @const 
9001  */
9002 Roo.form.Action.LOAD_FAILURE = 'load';
9003
9004 Roo.form.Action.prototype = {
9005     type : 'default',
9006     failureType : undefined,
9007     response : undefined,
9008     result : undefined,
9009
9010     // interface method
9011     run : function(options){
9012
9013     },
9014
9015     // interface method
9016     success : function(response){
9017
9018     },
9019
9020     // interface method
9021     handleResponse : function(response){
9022
9023     },
9024
9025     // default connection failure
9026     failure : function(response){
9027         
9028         this.response = response;
9029         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9030         this.form.afterAction(this, false);
9031     },
9032
9033     processResponse : function(response){
9034         this.response = response;
9035         if(!response.responseText){
9036             return true;
9037         }
9038         this.result = this.handleResponse(response);
9039         return this.result;
9040     },
9041
9042     // utility functions used internally
9043     getUrl : function(appendParams){
9044         var url = this.options.url || this.form.url || this.form.el.dom.action;
9045         if(appendParams){
9046             var p = this.getParams();
9047             if(p){
9048                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9049             }
9050         }
9051         return url;
9052     },
9053
9054     getMethod : function(){
9055         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9056     },
9057
9058     getParams : function(){
9059         var bp = this.form.baseParams;
9060         var p = this.options.params;
9061         if(p){
9062             if(typeof p == "object"){
9063                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9064             }else if(typeof p == 'string' && bp){
9065                 p += '&' + Roo.urlEncode(bp);
9066             }
9067         }else if(bp){
9068             p = Roo.urlEncode(bp);
9069         }
9070         return p;
9071     },
9072
9073     createCallback : function(){
9074         return {
9075             success: this.success,
9076             failure: this.failure,
9077             scope: this,
9078             timeout: (this.form.timeout*1000),
9079             upload: this.form.fileUpload ? this.success : undefined
9080         };
9081     }
9082 };
9083
9084 Roo.form.Action.Submit = function(form, options){
9085     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9086 };
9087
9088 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9089     type : 'submit',
9090
9091     haveProgress : false,
9092     uploadComplete : false,
9093     
9094     // uploadProgress indicator.
9095     uploadProgress : function()
9096     {
9097         if (!this.form.progressUrl) {
9098             return;
9099         }
9100         
9101         if (!this.haveProgress) {
9102             Roo.MessageBox.progress("Uploading", "Uploading");
9103         }
9104         if (this.uploadComplete) {
9105            Roo.MessageBox.hide();
9106            return;
9107         }
9108         
9109         this.haveProgress = true;
9110    
9111         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9112         
9113         var c = new Roo.data.Connection();
9114         c.request({
9115             url : this.form.progressUrl,
9116             params: {
9117                 id : uid
9118             },
9119             method: 'GET',
9120             success : function(req){
9121                //console.log(data);
9122                 var rdata = false;
9123                 var edata;
9124                 try  {
9125                    rdata = Roo.decode(req.responseText)
9126                 } catch (e) {
9127                     Roo.log("Invalid data from server..");
9128                     Roo.log(edata);
9129                     return;
9130                 }
9131                 if (!rdata || !rdata.success) {
9132                     Roo.log(rdata);
9133                     Roo.MessageBox.alert(Roo.encode(rdata));
9134                     return;
9135                 }
9136                 var data = rdata.data;
9137                 
9138                 if (this.uploadComplete) {
9139                    Roo.MessageBox.hide();
9140                    return;
9141                 }
9142                    
9143                 if (data){
9144                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9145                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9146                     );
9147                 }
9148                 this.uploadProgress.defer(2000,this);
9149             },
9150        
9151             failure: function(data) {
9152                 Roo.log('progress url failed ');
9153                 Roo.log(data);
9154             },
9155             scope : this
9156         });
9157            
9158     },
9159     
9160     
9161     run : function()
9162     {
9163         // run get Values on the form, so it syncs any secondary forms.
9164         this.form.getValues();
9165         
9166         var o = this.options;
9167         var method = this.getMethod();
9168         var isPost = method == 'POST';
9169         if(o.clientValidation === false || this.form.isValid()){
9170             
9171             if (this.form.progressUrl) {
9172                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9173                     (new Date() * 1) + '' + Math.random());
9174                     
9175             } 
9176             
9177             
9178             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9179                 form:this.form.el.dom,
9180                 url:this.getUrl(!isPost),
9181                 method: method,
9182                 params:isPost ? this.getParams() : null,
9183                 isUpload: this.form.fileUpload,
9184                 formData : this.form.formData
9185             }));
9186             
9187             this.uploadProgress();
9188
9189         }else if (o.clientValidation !== false){ // client validation failed
9190             this.failureType = Roo.form.Action.CLIENT_INVALID;
9191             this.form.afterAction(this, false);
9192         }
9193     },
9194
9195     success : function(response)
9196     {
9197         this.uploadComplete= true;
9198         if (this.haveProgress) {
9199             Roo.MessageBox.hide();
9200         }
9201         
9202         
9203         var result = this.processResponse(response);
9204         if(result === true || result.success){
9205             this.form.afterAction(this, true);
9206             return;
9207         }
9208         if(result.errors){
9209             this.form.markInvalid(result.errors);
9210             this.failureType = Roo.form.Action.SERVER_INVALID;
9211         }
9212         this.form.afterAction(this, false);
9213     },
9214     failure : function(response)
9215     {
9216         this.uploadComplete= true;
9217         if (this.haveProgress) {
9218             Roo.MessageBox.hide();
9219         }
9220         
9221         this.response = response;
9222         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9223         this.form.afterAction(this, false);
9224     },
9225     
9226     handleResponse : function(response){
9227         if(this.form.errorReader){
9228             var rs = this.form.errorReader.read(response);
9229             var errors = [];
9230             if(rs.records){
9231                 for(var i = 0, len = rs.records.length; i < len; i++) {
9232                     var r = rs.records[i];
9233                     errors[i] = r.data;
9234                 }
9235             }
9236             if(errors.length < 1){
9237                 errors = null;
9238             }
9239             return {
9240                 success : rs.success,
9241                 errors : errors
9242             };
9243         }
9244         var ret = false;
9245         try {
9246             ret = Roo.decode(response.responseText);
9247         } catch (e) {
9248             ret = {
9249                 success: false,
9250                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9251                 errors : []
9252             };
9253         }
9254         return ret;
9255         
9256     }
9257 });
9258
9259
9260 Roo.form.Action.Load = function(form, options){
9261     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9262     this.reader = this.form.reader;
9263 };
9264
9265 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9266     type : 'load',
9267
9268     run : function(){
9269         
9270         Roo.Ajax.request(Roo.apply(
9271                 this.createCallback(), {
9272                     method:this.getMethod(),
9273                     url:this.getUrl(false),
9274                     params:this.getParams()
9275         }));
9276     },
9277
9278     success : function(response){
9279         
9280         var result = this.processResponse(response);
9281         if(result === true || !result.success || !result.data){
9282             this.failureType = Roo.form.Action.LOAD_FAILURE;
9283             this.form.afterAction(this, false);
9284             return;
9285         }
9286         this.form.clearInvalid();
9287         this.form.setValues(result.data);
9288         this.form.afterAction(this, true);
9289     },
9290
9291     handleResponse : function(response){
9292         if(this.form.reader){
9293             var rs = this.form.reader.read(response);
9294             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9295             return {
9296                 success : rs.success,
9297                 data : data
9298             };
9299         }
9300         return Roo.decode(response.responseText);
9301     }
9302 });
9303
9304 Roo.form.Action.ACTION_TYPES = {
9305     'load' : Roo.form.Action.Load,
9306     'submit' : Roo.form.Action.Submit
9307 };/*
9308  * - LGPL
9309  *
9310  * form
9311  *
9312  */
9313
9314 /**
9315  * @class Roo.bootstrap.Form
9316  * @extends Roo.bootstrap.Component
9317  * Bootstrap Form class
9318  * @cfg {String} method  GET | POST (default POST)
9319  * @cfg {String} labelAlign top | left (default top)
9320  * @cfg {String} align left  | right - for navbars
9321  * @cfg {Boolean} loadMask load mask when submit (default true)
9322
9323  *
9324  * @constructor
9325  * Create a new Form
9326  * @param {Object} config The config object
9327  */
9328
9329
9330 Roo.bootstrap.Form = function(config){
9331     
9332     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9333     
9334     Roo.bootstrap.Form.popover.apply();
9335     
9336     this.addEvents({
9337         /**
9338          * @event clientvalidation
9339          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9340          * @param {Form} this
9341          * @param {Boolean} valid true if the form has passed client-side validation
9342          */
9343         clientvalidation: true,
9344         /**
9345          * @event beforeaction
9346          * Fires before any action is performed. Return false to cancel the action.
9347          * @param {Form} this
9348          * @param {Action} action The action to be performed
9349          */
9350         beforeaction: true,
9351         /**
9352          * @event actionfailed
9353          * Fires when an action fails.
9354          * @param {Form} this
9355          * @param {Action} action The action that failed
9356          */
9357         actionfailed : true,
9358         /**
9359          * @event actioncomplete
9360          * Fires when an action is completed.
9361          * @param {Form} this
9362          * @param {Action} action The action that completed
9363          */
9364         actioncomplete : true
9365     });
9366 };
9367
9368 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9369
9370      /**
9371      * @cfg {String} method
9372      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9373      */
9374     method : 'POST',
9375     /**
9376      * @cfg {String} url
9377      * The URL to use for form actions if one isn't supplied in the action options.
9378      */
9379     /**
9380      * @cfg {Boolean} fileUpload
9381      * Set to true if this form is a file upload.
9382      */
9383
9384     /**
9385      * @cfg {Object} baseParams
9386      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9387      */
9388
9389     /**
9390      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9391      */
9392     timeout: 30,
9393     /**
9394      * @cfg {Sting} align (left|right) for navbar forms
9395      */
9396     align : 'left',
9397
9398     // private
9399     activeAction : null,
9400
9401     /**
9402      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9403      * element by passing it or its id or mask the form itself by passing in true.
9404      * @type Mixed
9405      */
9406     waitMsgTarget : false,
9407
9408     loadMask : true,
9409     
9410     /**
9411      * @cfg {Boolean} errorMask (true|false) default false
9412      */
9413     errorMask : false,
9414     
9415     /**
9416      * @cfg {Number} maskOffset Default 100
9417      */
9418     maskOffset : 100,
9419     
9420     /**
9421      * @cfg {Boolean} maskBody
9422      */
9423     maskBody : false,
9424
9425     getAutoCreate : function(){
9426
9427         var cfg = {
9428             tag: 'form',
9429             method : this.method || 'POST',
9430             id : this.id || Roo.id(),
9431             cls : ''
9432         };
9433         if (this.parent().xtype.match(/^Nav/)) {
9434             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9435
9436         }
9437
9438         if (this.labelAlign == 'left' ) {
9439             cfg.cls += ' form-horizontal';
9440         }
9441
9442
9443         return cfg;
9444     },
9445     initEvents : function()
9446     {
9447         this.el.on('submit', this.onSubmit, this);
9448         // this was added as random key presses on the form where triggering form submit.
9449         this.el.on('keypress', function(e) {
9450             if (e.getCharCode() != 13) {
9451                 return true;
9452             }
9453             // we might need to allow it for textareas.. and some other items.
9454             // check e.getTarget().
9455
9456             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9457                 return true;
9458             }
9459
9460             Roo.log("keypress blocked");
9461
9462             e.preventDefault();
9463             return false;
9464         });
9465         
9466     },
9467     // private
9468     onSubmit : function(e){
9469         e.stopEvent();
9470     },
9471
9472      /**
9473      * Returns true if client-side validation on the form is successful.
9474      * @return Boolean
9475      */
9476     isValid : function(){
9477         var items = this.getItems();
9478         var valid = true;
9479         var target = false;
9480         
9481         items.each(function(f){
9482             
9483             if(f.validate()){
9484                 return;
9485             }
9486             
9487             Roo.log('invalid field: ' + f.name);
9488             
9489             valid = false;
9490
9491             if(!target && f.el.isVisible(true)){
9492                 target = f;
9493             }
9494            
9495         });
9496         
9497         if(this.errorMask && !valid){
9498             Roo.bootstrap.Form.popover.mask(this, target);
9499         }
9500         
9501         return valid;
9502     },
9503     
9504     /**
9505      * Returns true if any fields in this form have changed since their original load.
9506      * @return Boolean
9507      */
9508     isDirty : function(){
9509         var dirty = false;
9510         var items = this.getItems();
9511         items.each(function(f){
9512            if(f.isDirty()){
9513                dirty = true;
9514                return false;
9515            }
9516            return true;
9517         });
9518         return dirty;
9519     },
9520      /**
9521      * Performs a predefined action (submit or load) or custom actions you define on this form.
9522      * @param {String} actionName The name of the action type
9523      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9524      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9525      * accept other config options):
9526      * <pre>
9527 Property          Type             Description
9528 ----------------  ---------------  ----------------------------------------------------------------------------------
9529 url               String           The url for the action (defaults to the form's url)
9530 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9531 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9532 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9533                                    validate the form on the client (defaults to false)
9534      * </pre>
9535      * @return {BasicForm} this
9536      */
9537     doAction : function(action, options){
9538         if(typeof action == 'string'){
9539             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9540         }
9541         if(this.fireEvent('beforeaction', this, action) !== false){
9542             this.beforeAction(action);
9543             action.run.defer(100, action);
9544         }
9545         return this;
9546     },
9547
9548     // private
9549     beforeAction : function(action){
9550         var o = action.options;
9551         
9552         if(this.loadMask){
9553             
9554             if(this.maskBody){
9555                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9556             } else {
9557                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9558             }
9559         }
9560         // not really supported yet.. ??
9561
9562         //if(this.waitMsgTarget === true){
9563         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9564         //}else if(this.waitMsgTarget){
9565         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9566         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9567         //}else {
9568         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9569        // }
9570
9571     },
9572
9573     // private
9574     afterAction : function(action, success){
9575         this.activeAction = null;
9576         var o = action.options;
9577
9578         if(this.loadMask){
9579             
9580             if(this.maskBody){
9581                 Roo.get(document.body).unmask();
9582             } else {
9583                 this.el.unmask();
9584             }
9585         }
9586         
9587         //if(this.waitMsgTarget === true){
9588 //            this.el.unmask();
9589         //}else if(this.waitMsgTarget){
9590         //    this.waitMsgTarget.unmask();
9591         //}else{
9592         //    Roo.MessageBox.updateProgress(1);
9593         //    Roo.MessageBox.hide();
9594        // }
9595         //
9596         if(success){
9597             if(o.reset){
9598                 this.reset();
9599             }
9600             Roo.callback(o.success, o.scope, [this, action]);
9601             this.fireEvent('actioncomplete', this, action);
9602
9603         }else{
9604
9605             // failure condition..
9606             // we have a scenario where updates need confirming.
9607             // eg. if a locking scenario exists..
9608             // we look for { errors : { needs_confirm : true }} in the response.
9609             if (
9610                 (typeof(action.result) != 'undefined')  &&
9611                 (typeof(action.result.errors) != 'undefined')  &&
9612                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9613            ){
9614                 var _t = this;
9615                 Roo.log("not supported yet");
9616                  /*
9617
9618                 Roo.MessageBox.confirm(
9619                     "Change requires confirmation",
9620                     action.result.errorMsg,
9621                     function(r) {
9622                         if (r != 'yes') {
9623                             return;
9624                         }
9625                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9626                     }
9627
9628                 );
9629                 */
9630
9631
9632                 return;
9633             }
9634
9635             Roo.callback(o.failure, o.scope, [this, action]);
9636             // show an error message if no failed handler is set..
9637             if (!this.hasListener('actionfailed')) {
9638                 Roo.log("need to add dialog support");
9639                 /*
9640                 Roo.MessageBox.alert("Error",
9641                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9642                         action.result.errorMsg :
9643                         "Saving Failed, please check your entries or try again"
9644                 );
9645                 */
9646             }
9647
9648             this.fireEvent('actionfailed', this, action);
9649         }
9650
9651     },
9652     /**
9653      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9654      * @param {String} id The value to search for
9655      * @return Field
9656      */
9657     findField : function(id){
9658         var items = this.getItems();
9659         var field = items.get(id);
9660         if(!field){
9661              items.each(function(f){
9662                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9663                     field = f;
9664                     return false;
9665                 }
9666                 return true;
9667             });
9668         }
9669         return field || null;
9670     },
9671      /**
9672      * Mark fields in this form invalid in bulk.
9673      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9674      * @return {BasicForm} this
9675      */
9676     markInvalid : function(errors){
9677         if(errors instanceof Array){
9678             for(var i = 0, len = errors.length; i < len; i++){
9679                 var fieldError = errors[i];
9680                 var f = this.findField(fieldError.id);
9681                 if(f){
9682                     f.markInvalid(fieldError.msg);
9683                 }
9684             }
9685         }else{
9686             var field, id;
9687             for(id in errors){
9688                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9689                     field.markInvalid(errors[id]);
9690                 }
9691             }
9692         }
9693         //Roo.each(this.childForms || [], function (f) {
9694         //    f.markInvalid(errors);
9695         //});
9696
9697         return this;
9698     },
9699
9700     /**
9701      * Set values for fields in this form in bulk.
9702      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9703      * @return {BasicForm} this
9704      */
9705     setValues : function(values){
9706         if(values instanceof Array){ // array of objects
9707             for(var i = 0, len = values.length; i < len; i++){
9708                 var v = values[i];
9709                 var f = this.findField(v.id);
9710                 if(f){
9711                     f.setValue(v.value);
9712                     if(this.trackResetOnLoad){
9713                         f.originalValue = f.getValue();
9714                     }
9715                 }
9716             }
9717         }else{ // object hash
9718             var field, id;
9719             for(id in values){
9720                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9721
9722                     if (field.setFromData &&
9723                         field.valueField &&
9724                         field.displayField &&
9725                         // combos' with local stores can
9726                         // be queried via setValue()
9727                         // to set their value..
9728                         (field.store && !field.store.isLocal)
9729                         ) {
9730                         // it's a combo
9731                         var sd = { };
9732                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9733                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9734                         field.setFromData(sd);
9735
9736                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9737                         
9738                         field.setFromData(values);
9739                         
9740                     } else {
9741                         field.setValue(values[id]);
9742                     }
9743
9744
9745                     if(this.trackResetOnLoad){
9746                         field.originalValue = field.getValue();
9747                     }
9748                 }
9749             }
9750         }
9751
9752         //Roo.each(this.childForms || [], function (f) {
9753         //    f.setValues(values);
9754         //});
9755
9756         return this;
9757     },
9758
9759     /**
9760      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9761      * they are returned as an array.
9762      * @param {Boolean} asString
9763      * @return {Object}
9764      */
9765     getValues : function(asString){
9766         //if (this.childForms) {
9767             // copy values from the child forms
9768         //    Roo.each(this.childForms, function (f) {
9769         //        this.setValues(f.getValues());
9770         //    }, this);
9771         //}
9772
9773
9774
9775         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9776         if(asString === true){
9777             return fs;
9778         }
9779         return Roo.urlDecode(fs);
9780     },
9781
9782     /**
9783      * Returns the fields in this form as an object with key/value pairs.
9784      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9785      * @return {Object}
9786      */
9787     getFieldValues : function(with_hidden)
9788     {
9789         var items = this.getItems();
9790         var ret = {};
9791         items.each(function(f){
9792             
9793             if (!f.getName()) {
9794                 return;
9795             }
9796             
9797             var v = f.getValue();
9798             
9799             if (f.inputType =='radio') {
9800                 if (typeof(ret[f.getName()]) == 'undefined') {
9801                     ret[f.getName()] = ''; // empty..
9802                 }
9803
9804                 if (!f.el.dom.checked) {
9805                     return;
9806
9807                 }
9808                 v = f.el.dom.value;
9809
9810             }
9811             
9812             if(f.xtype == 'MoneyField'){
9813                 ret[f.currencyName] = f.getCurrency();
9814             }
9815
9816             // not sure if this supported any more..
9817             if ((typeof(v) == 'object') && f.getRawValue) {
9818                 v = f.getRawValue() ; // dates..
9819             }
9820             // combo boxes where name != hiddenName...
9821             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9822                 ret[f.name] = f.getRawValue();
9823             }
9824             ret[f.getName()] = v;
9825         });
9826
9827         return ret;
9828     },
9829
9830     /**
9831      * Clears all invalid messages in this form.
9832      * @return {BasicForm} this
9833      */
9834     clearInvalid : function(){
9835         var items = this.getItems();
9836
9837         items.each(function(f){
9838            f.clearInvalid();
9839         });
9840
9841         return this;
9842     },
9843
9844     /**
9845      * Resets this form.
9846      * @return {BasicForm} this
9847      */
9848     reset : function(){
9849         var items = this.getItems();
9850         items.each(function(f){
9851             f.reset();
9852         });
9853
9854         Roo.each(this.childForms || [], function (f) {
9855             f.reset();
9856         });
9857
9858
9859         return this;
9860     },
9861     
9862     getItems : function()
9863     {
9864         var r=new Roo.util.MixedCollection(false, function(o){
9865             return o.id || (o.id = Roo.id());
9866         });
9867         var iter = function(el) {
9868             if (el.inputEl) {
9869                 r.add(el);
9870             }
9871             if (!el.items) {
9872                 return;
9873             }
9874             Roo.each(el.items,function(e) {
9875                 iter(e);
9876             });
9877         };
9878
9879         iter(this);
9880         return r;
9881     },
9882     
9883     hideFields : function(items)
9884     {
9885         Roo.each(items, function(i){
9886             
9887             var f = this.findField(i);
9888             
9889             if(!f){
9890                 return;
9891             }
9892             
9893             f.hide();
9894             
9895         }, this);
9896     },
9897     
9898     showFields : function(items)
9899     {
9900         Roo.each(items, function(i){
9901             
9902             var f = this.findField(i);
9903             
9904             if(!f){
9905                 return;
9906             }
9907             
9908             f.show();
9909             
9910         }, this);
9911     }
9912
9913 });
9914
9915 Roo.apply(Roo.bootstrap.Form, {
9916     
9917     popover : {
9918         
9919         padding : 5,
9920         
9921         isApplied : false,
9922         
9923         isMasked : false,
9924         
9925         form : false,
9926         
9927         target : false,
9928         
9929         toolTip : false,
9930         
9931         intervalID : false,
9932         
9933         maskEl : false,
9934         
9935         apply : function()
9936         {
9937             if(this.isApplied){
9938                 return;
9939             }
9940             
9941             this.maskEl = {
9942                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9943                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9944                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9945                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9946             };
9947             
9948             this.maskEl.top.enableDisplayMode("block");
9949             this.maskEl.left.enableDisplayMode("block");
9950             this.maskEl.bottom.enableDisplayMode("block");
9951             this.maskEl.right.enableDisplayMode("block");
9952             
9953             this.toolTip = new Roo.bootstrap.Tooltip({
9954                 cls : 'roo-form-error-popover',
9955                 alignment : {
9956                     'left' : ['r-l', [-2,0], 'right'],
9957                     'right' : ['l-r', [2,0], 'left'],
9958                     'bottom' : ['tl-bl', [0,2], 'top'],
9959                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9960                 }
9961             });
9962             
9963             this.toolTip.render(Roo.get(document.body));
9964
9965             this.toolTip.el.enableDisplayMode("block");
9966             
9967             Roo.get(document.body).on('click', function(){
9968                 this.unmask();
9969             }, this);
9970             
9971             Roo.get(document.body).on('touchstart', function(){
9972                 this.unmask();
9973             }, this);
9974             
9975             this.isApplied = true
9976         },
9977         
9978         mask : function(form, target)
9979         {
9980             this.form = form;
9981             
9982             this.target = target;
9983             
9984             if(!this.form.errorMask || !target.el){
9985                 return;
9986             }
9987             
9988             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9989             
9990             Roo.log(scrollable);
9991             
9992             var ot = this.target.el.calcOffsetsTo(scrollable);
9993             
9994             var scrollTo = ot[1] - this.form.maskOffset;
9995             
9996             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
9997             
9998             scrollable.scrollTo('top', scrollTo);
9999             
10000             var box = this.target.el.getBox();
10001             Roo.log(box);
10002             var zIndex = Roo.bootstrap.Modal.zIndex++;
10003
10004             
10005             this.maskEl.top.setStyle('position', 'absolute');
10006             this.maskEl.top.setStyle('z-index', zIndex);
10007             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10008             this.maskEl.top.setLeft(0);
10009             this.maskEl.top.setTop(0);
10010             this.maskEl.top.show();
10011             
10012             this.maskEl.left.setStyle('position', 'absolute');
10013             this.maskEl.left.setStyle('z-index', zIndex);
10014             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10015             this.maskEl.left.setLeft(0);
10016             this.maskEl.left.setTop(box.y - this.padding);
10017             this.maskEl.left.show();
10018
10019             this.maskEl.bottom.setStyle('position', 'absolute');
10020             this.maskEl.bottom.setStyle('z-index', zIndex);
10021             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10022             this.maskEl.bottom.setLeft(0);
10023             this.maskEl.bottom.setTop(box.bottom + this.padding);
10024             this.maskEl.bottom.show();
10025
10026             this.maskEl.right.setStyle('position', 'absolute');
10027             this.maskEl.right.setStyle('z-index', zIndex);
10028             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10029             this.maskEl.right.setLeft(box.right + this.padding);
10030             this.maskEl.right.setTop(box.y - this.padding);
10031             this.maskEl.right.show();
10032
10033             this.toolTip.bindEl = this.target.el;
10034
10035             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10036
10037             var tip = this.target.blankText;
10038
10039             if(this.target.getValue() !== '' ) {
10040                 
10041                 if (this.target.invalidText.length) {
10042                     tip = this.target.invalidText;
10043                 } else if (this.target.regexText.length){
10044                     tip = this.target.regexText;
10045                 }
10046             }
10047
10048             this.toolTip.show(tip);
10049
10050             this.intervalID = window.setInterval(function() {
10051                 Roo.bootstrap.Form.popover.unmask();
10052             }, 10000);
10053
10054             window.onwheel = function(){ return false;};
10055             
10056             (function(){ this.isMasked = true; }).defer(500, this);
10057             
10058         },
10059         
10060         unmask : function()
10061         {
10062             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10063                 return;
10064             }
10065             
10066             this.maskEl.top.setStyle('position', 'absolute');
10067             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10068             this.maskEl.top.hide();
10069
10070             this.maskEl.left.setStyle('position', 'absolute');
10071             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10072             this.maskEl.left.hide();
10073
10074             this.maskEl.bottom.setStyle('position', 'absolute');
10075             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10076             this.maskEl.bottom.hide();
10077
10078             this.maskEl.right.setStyle('position', 'absolute');
10079             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10080             this.maskEl.right.hide();
10081             
10082             this.toolTip.hide();
10083             
10084             this.toolTip.el.hide();
10085             
10086             window.onwheel = function(){ return true;};
10087             
10088             if(this.intervalID){
10089                 window.clearInterval(this.intervalID);
10090                 this.intervalID = false;
10091             }
10092             
10093             this.isMasked = false;
10094             
10095         }
10096         
10097     }
10098     
10099 });
10100
10101 /*
10102  * Based on:
10103  * Ext JS Library 1.1.1
10104  * Copyright(c) 2006-2007, Ext JS, LLC.
10105  *
10106  * Originally Released Under LGPL - original licence link has changed is not relivant.
10107  *
10108  * Fork - LGPL
10109  * <script type="text/javascript">
10110  */
10111 /**
10112  * @class Roo.form.VTypes
10113  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10114  * @singleton
10115  */
10116 Roo.form.VTypes = function(){
10117     // closure these in so they are only created once.
10118     var alpha = /^[a-zA-Z_]+$/;
10119     var alphanum = /^[a-zA-Z0-9_]+$/;
10120     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10121     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10122
10123     // All these messages and functions are configurable
10124     return {
10125         /**
10126          * The function used to validate email addresses
10127          * @param {String} value The email address
10128          */
10129         'email' : function(v){
10130             return email.test(v);
10131         },
10132         /**
10133          * The error text to display when the email validation function returns false
10134          * @type String
10135          */
10136         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10137         /**
10138          * The keystroke filter mask to be applied on email input
10139          * @type RegExp
10140          */
10141         'emailMask' : /[a-z0-9_\.\-@]/i,
10142
10143         /**
10144          * The function used to validate URLs
10145          * @param {String} value The URL
10146          */
10147         'url' : function(v){
10148             return url.test(v);
10149         },
10150         /**
10151          * The error text to display when the url validation function returns false
10152          * @type String
10153          */
10154         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10155         
10156         /**
10157          * The function used to validate alpha values
10158          * @param {String} value The value
10159          */
10160         'alpha' : function(v){
10161             return alpha.test(v);
10162         },
10163         /**
10164          * The error text to display when the alpha validation function returns false
10165          * @type String
10166          */
10167         'alphaText' : 'This field should only contain letters and _',
10168         /**
10169          * The keystroke filter mask to be applied on alpha input
10170          * @type RegExp
10171          */
10172         'alphaMask' : /[a-z_]/i,
10173
10174         /**
10175          * The function used to validate alphanumeric values
10176          * @param {String} value The value
10177          */
10178         'alphanum' : function(v){
10179             return alphanum.test(v);
10180         },
10181         /**
10182          * The error text to display when the alphanumeric validation function returns false
10183          * @type String
10184          */
10185         'alphanumText' : 'This field should only contain letters, numbers and _',
10186         /**
10187          * The keystroke filter mask to be applied on alphanumeric input
10188          * @type RegExp
10189          */
10190         'alphanumMask' : /[a-z0-9_]/i
10191     };
10192 }();/*
10193  * - LGPL
10194  *
10195  * Input
10196  * 
10197  */
10198
10199 /**
10200  * @class Roo.bootstrap.Input
10201  * @extends Roo.bootstrap.Component
10202  * Bootstrap Input class
10203  * @cfg {Boolean} disabled is it disabled
10204  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10205  * @cfg {String} name name of the input
10206  * @cfg {string} fieldLabel - the label associated
10207  * @cfg {string} placeholder - placeholder to put in text.
10208  * @cfg {string}  before - input group add on before
10209  * @cfg {string} after - input group add on after
10210  * @cfg {string} size - (lg|sm) or leave empty..
10211  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10212  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10213  * @cfg {Number} md colspan out of 12 for computer-sized screens
10214  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10215  * @cfg {string} value default value of the input
10216  * @cfg {Number} labelWidth set the width of label 
10217  * @cfg {Number} labellg set the width of label (1-12)
10218  * @cfg {Number} labelmd set the width of label (1-12)
10219  * @cfg {Number} labelsm set the width of label (1-12)
10220  * @cfg {Number} labelxs set the width of label (1-12)
10221  * @cfg {String} labelAlign (top|left)
10222  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10223  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10224  * @cfg {String} indicatorpos (left|right) default left
10225  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10226  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10227  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10228
10229  * @cfg {String} align (left|center|right) Default left
10230  * @cfg {Boolean} forceFeedback (true|false) Default false
10231  * 
10232  * @constructor
10233  * Create a new Input
10234  * @param {Object} config The config object
10235  */
10236
10237 Roo.bootstrap.Input = function(config){
10238     
10239     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10240     
10241     this.addEvents({
10242         /**
10243          * @event focus
10244          * Fires when this field receives input focus.
10245          * @param {Roo.form.Field} this
10246          */
10247         focus : true,
10248         /**
10249          * @event blur
10250          * Fires when this field loses input focus.
10251          * @param {Roo.form.Field} this
10252          */
10253         blur : true,
10254         /**
10255          * @event specialkey
10256          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10257          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10258          * @param {Roo.form.Field} this
10259          * @param {Roo.EventObject} e The event object
10260          */
10261         specialkey : true,
10262         /**
10263          * @event change
10264          * Fires just before the field blurs if the field value has changed.
10265          * @param {Roo.form.Field} this
10266          * @param {Mixed} newValue The new value
10267          * @param {Mixed} oldValue The original value
10268          */
10269         change : true,
10270         /**
10271          * @event invalid
10272          * Fires after the field has been marked as invalid.
10273          * @param {Roo.form.Field} this
10274          * @param {String} msg The validation message
10275          */
10276         invalid : true,
10277         /**
10278          * @event valid
10279          * Fires after the field has been validated with no errors.
10280          * @param {Roo.form.Field} this
10281          */
10282         valid : true,
10283          /**
10284          * @event keyup
10285          * Fires after the key up
10286          * @param {Roo.form.Field} this
10287          * @param {Roo.EventObject}  e The event Object
10288          */
10289         keyup : true
10290     });
10291 };
10292
10293 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10294      /**
10295      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10296       automatic validation (defaults to "keyup").
10297      */
10298     validationEvent : "keyup",
10299      /**
10300      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10301      */
10302     validateOnBlur : true,
10303     /**
10304      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10305      */
10306     validationDelay : 250,
10307      /**
10308      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10309      */
10310     focusClass : "x-form-focus",  // not needed???
10311     
10312        
10313     /**
10314      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10315      */
10316     invalidClass : "has-warning",
10317     
10318     /**
10319      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10320      */
10321     validClass : "has-success",
10322     
10323     /**
10324      * @cfg {Boolean} hasFeedback (true|false) default true
10325      */
10326     hasFeedback : true,
10327     
10328     /**
10329      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10330      */
10331     invalidFeedbackClass : "glyphicon-warning-sign",
10332     
10333     /**
10334      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10335      */
10336     validFeedbackClass : "glyphicon-ok",
10337     
10338     /**
10339      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10340      */
10341     selectOnFocus : false,
10342     
10343      /**
10344      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10345      */
10346     maskRe : null,
10347        /**
10348      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10349      */
10350     vtype : null,
10351     
10352       /**
10353      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10354      */
10355     disableKeyFilter : false,
10356     
10357        /**
10358      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10359      */
10360     disabled : false,
10361      /**
10362      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10363      */
10364     allowBlank : true,
10365     /**
10366      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10367      */
10368     blankText : "Please complete this mandatory field",
10369     
10370      /**
10371      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10372      */
10373     minLength : 0,
10374     /**
10375      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10376      */
10377     maxLength : Number.MAX_VALUE,
10378     /**
10379      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10380      */
10381     minLengthText : "The minimum length for this field is {0}",
10382     /**
10383      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10384      */
10385     maxLengthText : "The maximum length for this field is {0}",
10386   
10387     
10388     /**
10389      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10390      * If available, this function will be called only after the basic validators all return true, and will be passed the
10391      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10392      */
10393     validator : null,
10394     /**
10395      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10396      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10397      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10398      */
10399     regex : null,
10400     /**
10401      * @cfg {String} regexText -- Depricated - use Invalid Text
10402      */
10403     regexText : "",
10404     
10405     /**
10406      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10407      */
10408     invalidText : "",
10409     
10410     
10411     
10412     autocomplete: false,
10413     
10414     
10415     fieldLabel : '',
10416     inputType : 'text',
10417     
10418     name : false,
10419     placeholder: false,
10420     before : false,
10421     after : false,
10422     size : false,
10423     hasFocus : false,
10424     preventMark: false,
10425     isFormField : true,
10426     value : '',
10427     labelWidth : 2,
10428     labelAlign : false,
10429     readOnly : false,
10430     align : false,
10431     formatedValue : false,
10432     forceFeedback : false,
10433     
10434     indicatorpos : 'left',
10435     
10436     labellg : 0,
10437     labelmd : 0,
10438     labelsm : 0,
10439     labelxs : 0,
10440     
10441     capture : '',
10442     accept : '',
10443     
10444     parentLabelAlign : function()
10445     {
10446         var parent = this;
10447         while (parent.parent()) {
10448             parent = parent.parent();
10449             if (typeof(parent.labelAlign) !='undefined') {
10450                 return parent.labelAlign;
10451             }
10452         }
10453         return 'left';
10454         
10455     },
10456     
10457     getAutoCreate : function()
10458     {
10459         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10460         
10461         var id = Roo.id();
10462         
10463         var cfg = {};
10464         
10465         if(this.inputType != 'hidden'){
10466             cfg.cls = 'form-group' //input-group
10467         }
10468         
10469         var input =  {
10470             tag: 'input',
10471             id : id,
10472             type : this.inputType,
10473             value : this.value,
10474             cls : 'form-control',
10475             placeholder : this.placeholder || '',
10476             autocomplete : this.autocomplete || 'new-password'
10477         };
10478         
10479         if(this.capture.length){
10480             input.capture = this.capture;
10481         }
10482         
10483         if(this.accept.length){
10484             input.accept = this.accept + "/*";
10485         }
10486         
10487         if(this.align){
10488             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10489         }
10490         
10491         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10492             input.maxLength = this.maxLength;
10493         }
10494         
10495         if (this.disabled) {
10496             input.disabled=true;
10497         }
10498         
10499         if (this.readOnly) {
10500             input.readonly=true;
10501         }
10502         
10503         if (this.name) {
10504             input.name = this.name;
10505         }
10506         
10507         if (this.size) {
10508             input.cls += ' input-' + this.size;
10509         }
10510         
10511         var settings=this;
10512         ['xs','sm','md','lg'].map(function(size){
10513             if (settings[size]) {
10514                 cfg.cls += ' col-' + size + '-' + settings[size];
10515             }
10516         });
10517         
10518         var inputblock = input;
10519         
10520         var feedback = {
10521             tag: 'span',
10522             cls: 'glyphicon form-control-feedback'
10523         };
10524             
10525         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10526             
10527             inputblock = {
10528                 cls : 'has-feedback',
10529                 cn :  [
10530                     input,
10531                     feedback
10532                 ] 
10533             };  
10534         }
10535         
10536         if (this.before || this.after) {
10537             
10538             inputblock = {
10539                 cls : 'input-group',
10540                 cn :  [] 
10541             };
10542             
10543             if (this.before && typeof(this.before) == 'string') {
10544                 
10545                 inputblock.cn.push({
10546                     tag :'span',
10547                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10548                     html : this.before
10549                 });
10550             }
10551             if (this.before && typeof(this.before) == 'object') {
10552                 this.before = Roo.factory(this.before);
10553                 
10554                 inputblock.cn.push({
10555                     tag :'span',
10556                     cls : 'roo-input-before input-group-prepend   input-group-' +
10557                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10558                 });
10559             }
10560             
10561             inputblock.cn.push(input);
10562             
10563             if (this.after && typeof(this.after) == 'string') {
10564                 inputblock.cn.push({
10565                     tag :'span',
10566                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10567                     html : this.after
10568                 });
10569             }
10570             if (this.after && typeof(this.after) == 'object') {
10571                 this.after = Roo.factory(this.after);
10572                 
10573                 inputblock.cn.push({
10574                     tag :'span',
10575                     cls : 'roo-input-after input-group-append  input-group-' +
10576                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10577                 });
10578             }
10579             
10580             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10581                 inputblock.cls += ' has-feedback';
10582                 inputblock.cn.push(feedback);
10583             }
10584         };
10585         var indicator = {
10586             tag : 'i',
10587             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10588             tooltip : 'This field is required'
10589         };
10590         if (Roo.bootstrap.version == 4) {
10591             indicator = {
10592                 tag : 'i',
10593                 style : 'display-none'
10594             };
10595         }
10596         if (align ==='left' && this.fieldLabel.length) {
10597             
10598             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10599             
10600             cfg.cn = [
10601                 indicator,
10602                 {
10603                     tag: 'label',
10604                     'for' :  id,
10605                     cls : 'control-label col-form-label',
10606                     html : this.fieldLabel
10607
10608                 },
10609                 {
10610                     cls : "", 
10611                     cn: [
10612                         inputblock
10613                     ]
10614                 }
10615             ];
10616             
10617             var labelCfg = cfg.cn[1];
10618             var contentCfg = cfg.cn[2];
10619             
10620             if(this.indicatorpos == 'right'){
10621                 cfg.cn = [
10622                     {
10623                         tag: 'label',
10624                         'for' :  id,
10625                         cls : 'control-label col-form-label',
10626                         cn : [
10627                             {
10628                                 tag : 'span',
10629                                 html : this.fieldLabel
10630                             },
10631                             indicator
10632                         ]
10633                     },
10634                     {
10635                         cls : "",
10636                         cn: [
10637                             inputblock
10638                         ]
10639                     }
10640
10641                 ];
10642                 
10643                 labelCfg = cfg.cn[0];
10644                 contentCfg = cfg.cn[1];
10645             
10646             }
10647             
10648             if(this.labelWidth > 12){
10649                 labelCfg.style = "width: " + this.labelWidth + 'px';
10650             }
10651             
10652             if(this.labelWidth < 13 && this.labelmd == 0){
10653                 this.labelmd = this.labelWidth;
10654             }
10655             
10656             if(this.labellg > 0){
10657                 labelCfg.cls += ' col-lg-' + this.labellg;
10658                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10659             }
10660             
10661             if(this.labelmd > 0){
10662                 labelCfg.cls += ' col-md-' + this.labelmd;
10663                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10664             }
10665             
10666             if(this.labelsm > 0){
10667                 labelCfg.cls += ' col-sm-' + this.labelsm;
10668                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10669             }
10670             
10671             if(this.labelxs > 0){
10672                 labelCfg.cls += ' col-xs-' + this.labelxs;
10673                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10674             }
10675             
10676             
10677         } else if ( this.fieldLabel.length) {
10678                 
10679             cfg.cn = [
10680                 {
10681                     tag : 'i',
10682                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10683                     tooltip : 'This field is required'
10684                 },
10685                 {
10686                     tag: 'label',
10687                    //cls : 'input-group-addon',
10688                     html : this.fieldLabel
10689
10690                 },
10691
10692                inputblock
10693
10694            ];
10695            
10696            if(this.indicatorpos == 'right'){
10697                 
10698                 cfg.cn = [
10699                     {
10700                         tag: 'label',
10701                        //cls : 'input-group-addon',
10702                         html : this.fieldLabel
10703
10704                     },
10705                     {
10706                         tag : 'i',
10707                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10708                         tooltip : 'This field is required'
10709                     },
10710
10711                    inputblock
10712
10713                ];
10714
10715             }
10716
10717         } else {
10718             
10719             cfg.cn = [
10720
10721                     inputblock
10722
10723             ];
10724                 
10725                 
10726         };
10727         
10728         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10729            cfg.cls += ' navbar-form';
10730         }
10731         
10732         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10733             // on BS4 we do this only if not form 
10734             cfg.cls += ' navbar-form';
10735             cfg.tag = 'li';
10736         }
10737         
10738         return cfg;
10739         
10740     },
10741     /**
10742      * return the real input element.
10743      */
10744     inputEl: function ()
10745     {
10746         return this.el.select('input.form-control',true).first();
10747     },
10748     
10749     tooltipEl : function()
10750     {
10751         return this.inputEl();
10752     },
10753     
10754     indicatorEl : function()
10755     {
10756         if (Roo.bootstrap.version == 4) {
10757             return false; // not enabled in v4 yet.
10758         }
10759         
10760         var indicator = this.el.select('i.roo-required-indicator',true).first();
10761         
10762         if(!indicator){
10763             return false;
10764         }
10765         
10766         return indicator;
10767         
10768     },
10769     
10770     setDisabled : function(v)
10771     {
10772         var i  = this.inputEl().dom;
10773         if (!v) {
10774             i.removeAttribute('disabled');
10775             return;
10776             
10777         }
10778         i.setAttribute('disabled','true');
10779     },
10780     initEvents : function()
10781     {
10782           
10783         this.inputEl().on("keydown" , this.fireKey,  this);
10784         this.inputEl().on("focus", this.onFocus,  this);
10785         this.inputEl().on("blur", this.onBlur,  this);
10786         
10787         this.inputEl().relayEvent('keyup', this);
10788         
10789         this.indicator = this.indicatorEl();
10790         
10791         if(this.indicator){
10792             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10793         }
10794  
10795         // reference to original value for reset
10796         this.originalValue = this.getValue();
10797         //Roo.form.TextField.superclass.initEvents.call(this);
10798         if(this.validationEvent == 'keyup'){
10799             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10800             this.inputEl().on('keyup', this.filterValidation, this);
10801         }
10802         else if(this.validationEvent !== false){
10803             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10804         }
10805         
10806         if(this.selectOnFocus){
10807             this.on("focus", this.preFocus, this);
10808             
10809         }
10810         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10811             this.inputEl().on("keypress", this.filterKeys, this);
10812         } else {
10813             this.inputEl().relayEvent('keypress', this);
10814         }
10815        /* if(this.grow){
10816             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10817             this.el.on("click", this.autoSize,  this);
10818         }
10819         */
10820         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10821             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10822         }
10823         
10824         if (typeof(this.before) == 'object') {
10825             this.before.render(this.el.select('.roo-input-before',true).first());
10826         }
10827         if (typeof(this.after) == 'object') {
10828             this.after.render(this.el.select('.roo-input-after',true).first());
10829         }
10830         
10831         this.inputEl().on('change', this.onChange, this);
10832         
10833     },
10834     filterValidation : function(e){
10835         if(!e.isNavKeyPress()){
10836             this.validationTask.delay(this.validationDelay);
10837         }
10838     },
10839      /**
10840      * Validates the field value
10841      * @return {Boolean} True if the value is valid, else false
10842      */
10843     validate : function(){
10844         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10845         if(this.disabled || this.validateValue(this.getRawValue())){
10846             this.markValid();
10847             return true;
10848         }
10849         
10850         this.markInvalid();
10851         return false;
10852     },
10853     
10854     
10855     /**
10856      * Validates a value according to the field's validation rules and marks the field as invalid
10857      * if the validation fails
10858      * @param {Mixed} value The value to validate
10859      * @return {Boolean} True if the value is valid, else false
10860      */
10861     validateValue : function(value)
10862     {
10863         if(this.getVisibilityEl().hasClass('hidden')){
10864             return true;
10865         }
10866         
10867         if(value.length < 1)  { // if it's blank
10868             if(this.allowBlank){
10869                 return true;
10870             }
10871             return false;
10872         }
10873         
10874         if(value.length < this.minLength){
10875             return false;
10876         }
10877         if(value.length > this.maxLength){
10878             return false;
10879         }
10880         if(this.vtype){
10881             var vt = Roo.form.VTypes;
10882             if(!vt[this.vtype](value, this)){
10883                 return false;
10884             }
10885         }
10886         if(typeof this.validator == "function"){
10887             var msg = this.validator(value);
10888             if(msg !== true){
10889                 return false;
10890             }
10891             if (typeof(msg) == 'string') {
10892                 this.invalidText = msg;
10893             }
10894         }
10895         
10896         if(this.regex && !this.regex.test(value)){
10897             return false;
10898         }
10899         
10900         return true;
10901     },
10902     
10903      // private
10904     fireKey : function(e){
10905         //Roo.log('field ' + e.getKey());
10906         if(e.isNavKeyPress()){
10907             this.fireEvent("specialkey", this, e);
10908         }
10909     },
10910     focus : function (selectText){
10911         if(this.rendered){
10912             this.inputEl().focus();
10913             if(selectText === true){
10914                 this.inputEl().dom.select();
10915             }
10916         }
10917         return this;
10918     } ,
10919     
10920     onFocus : function(){
10921         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10922            // this.el.addClass(this.focusClass);
10923         }
10924         if(!this.hasFocus){
10925             this.hasFocus = true;
10926             this.startValue = this.getValue();
10927             this.fireEvent("focus", this);
10928         }
10929     },
10930     
10931     beforeBlur : Roo.emptyFn,
10932
10933     
10934     // private
10935     onBlur : function(){
10936         this.beforeBlur();
10937         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10938             //this.el.removeClass(this.focusClass);
10939         }
10940         this.hasFocus = false;
10941         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10942             this.validate();
10943         }
10944         var v = this.getValue();
10945         if(String(v) !== String(this.startValue)){
10946             this.fireEvent('change', this, v, this.startValue);
10947         }
10948         this.fireEvent("blur", this);
10949     },
10950     
10951     onChange : function(e)
10952     {
10953         var v = this.getValue();
10954         if(String(v) !== String(this.startValue)){
10955             this.fireEvent('change', this, v, this.startValue);
10956         }
10957         
10958     },
10959     
10960     /**
10961      * Resets the current field value to the originally loaded value and clears any validation messages
10962      */
10963     reset : function(){
10964         this.setValue(this.originalValue);
10965         this.validate();
10966     },
10967      /**
10968      * Returns the name of the field
10969      * @return {Mixed} name The name field
10970      */
10971     getName: function(){
10972         return this.name;
10973     },
10974      /**
10975      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10976      * @return {Mixed} value The field value
10977      */
10978     getValue : function(){
10979         
10980         var v = this.inputEl().getValue();
10981         
10982         return v;
10983     },
10984     /**
10985      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10986      * @return {Mixed} value The field value
10987      */
10988     getRawValue : function(){
10989         var v = this.inputEl().getValue();
10990         
10991         return v;
10992     },
10993     
10994     /**
10995      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
10996      * @param {Mixed} value The value to set
10997      */
10998     setRawValue : function(v){
10999         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11000     },
11001     
11002     selectText : function(start, end){
11003         var v = this.getRawValue();
11004         if(v.length > 0){
11005             start = start === undefined ? 0 : start;
11006             end = end === undefined ? v.length : end;
11007             var d = this.inputEl().dom;
11008             if(d.setSelectionRange){
11009                 d.setSelectionRange(start, end);
11010             }else if(d.createTextRange){
11011                 var range = d.createTextRange();
11012                 range.moveStart("character", start);
11013                 range.moveEnd("character", v.length-end);
11014                 range.select();
11015             }
11016         }
11017     },
11018     
11019     /**
11020      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11021      * @param {Mixed} value The value to set
11022      */
11023     setValue : function(v){
11024         this.value = v;
11025         if(this.rendered){
11026             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11027             this.validate();
11028         }
11029     },
11030     
11031     /*
11032     processValue : function(value){
11033         if(this.stripCharsRe){
11034             var newValue = value.replace(this.stripCharsRe, '');
11035             if(newValue !== value){
11036                 this.setRawValue(newValue);
11037                 return newValue;
11038             }
11039         }
11040         return value;
11041     },
11042   */
11043     preFocus : function(){
11044         
11045         if(this.selectOnFocus){
11046             this.inputEl().dom.select();
11047         }
11048     },
11049     filterKeys : function(e){
11050         var k = e.getKey();
11051         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11052             return;
11053         }
11054         var c = e.getCharCode(), cc = String.fromCharCode(c);
11055         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11056             return;
11057         }
11058         if(!this.maskRe.test(cc)){
11059             e.stopEvent();
11060         }
11061     },
11062      /**
11063      * Clear any invalid styles/messages for this field
11064      */
11065     clearInvalid : function(){
11066         
11067         if(!this.el || this.preventMark){ // not rendered
11068             return;
11069         }
11070         
11071         
11072         this.el.removeClass([this.invalidClass, 'is-invalid']);
11073         
11074         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11075             
11076             var feedback = this.el.select('.form-control-feedback', true).first();
11077             
11078             if(feedback){
11079                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11080             }
11081             
11082         }
11083         
11084         if(this.indicator){
11085             this.indicator.removeClass('visible');
11086             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11087         }
11088         
11089         this.fireEvent('valid', this);
11090     },
11091     
11092      /**
11093      * Mark this field as valid
11094      */
11095     markValid : function()
11096     {
11097         if(!this.el  || this.preventMark){ // not rendered...
11098             return;
11099         }
11100         
11101         this.el.removeClass([this.invalidClass, this.validClass]);
11102         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11103
11104         var feedback = this.el.select('.form-control-feedback', true).first();
11105             
11106         if(feedback){
11107             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11108         }
11109         
11110         if(this.indicator){
11111             this.indicator.removeClass('visible');
11112             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11113         }
11114         
11115         if(this.disabled){
11116             return;
11117         }
11118         
11119            
11120         if(this.allowBlank && !this.getRawValue().length){
11121             return;
11122         }
11123         if (Roo.bootstrap.version == 3) {
11124             this.el.addClass(this.validClass);
11125         } else {
11126             this.inputEl().addClass('is-valid');
11127         }
11128
11129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11130             
11131             var feedback = this.el.select('.form-control-feedback', true).first();
11132             
11133             if(feedback){
11134                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11135                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11136             }
11137             
11138         }
11139         
11140         this.fireEvent('valid', this);
11141     },
11142     
11143      /**
11144      * Mark this field as invalid
11145      * @param {String} msg The validation message
11146      */
11147     markInvalid : function(msg)
11148     {
11149         if(!this.el  || this.preventMark){ // not rendered
11150             return;
11151         }
11152         
11153         this.el.removeClass([this.invalidClass, this.validClass]);
11154         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11155         
11156         var feedback = this.el.select('.form-control-feedback', true).first();
11157             
11158         if(feedback){
11159             this.el.select('.form-control-feedback', true).first().removeClass(
11160                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11161         }
11162
11163         if(this.disabled){
11164             return;
11165         }
11166         
11167         if(this.allowBlank && !this.getRawValue().length){
11168             return;
11169         }
11170         
11171         if(this.indicator){
11172             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11173             this.indicator.addClass('visible');
11174         }
11175         if (Roo.bootstrap.version == 3) {
11176             this.el.addClass(this.invalidClass);
11177         } else {
11178             this.inputEl().addClass('is-invalid');
11179         }
11180         
11181         
11182         
11183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11184             
11185             var feedback = this.el.select('.form-control-feedback', true).first();
11186             
11187             if(feedback){
11188                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11189                 
11190                 if(this.getValue().length || this.forceFeedback){
11191                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11192                 }
11193                 
11194             }
11195             
11196         }
11197         
11198         this.fireEvent('invalid', this, msg);
11199     },
11200     // private
11201     SafariOnKeyDown : function(event)
11202     {
11203         // this is a workaround for a password hang bug on chrome/ webkit.
11204         if (this.inputEl().dom.type != 'password') {
11205             return;
11206         }
11207         
11208         var isSelectAll = false;
11209         
11210         if(this.inputEl().dom.selectionEnd > 0){
11211             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11212         }
11213         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11214             event.preventDefault();
11215             this.setValue('');
11216             return;
11217         }
11218         
11219         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11220             
11221             event.preventDefault();
11222             // this is very hacky as keydown always get's upper case.
11223             //
11224             var cc = String.fromCharCode(event.getCharCode());
11225             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11226             
11227         }
11228     },
11229     adjustWidth : function(tag, w){
11230         tag = tag.toLowerCase();
11231         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11232             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11233                 if(tag == 'input'){
11234                     return w + 2;
11235                 }
11236                 if(tag == 'textarea'){
11237                     return w-2;
11238                 }
11239             }else if(Roo.isOpera){
11240                 if(tag == 'input'){
11241                     return w + 2;
11242                 }
11243                 if(tag == 'textarea'){
11244                     return w-2;
11245                 }
11246             }
11247         }
11248         return w;
11249     },
11250     
11251     setFieldLabel : function(v)
11252     {
11253         if(!this.rendered){
11254             return;
11255         }
11256         
11257         if(this.indicatorEl()){
11258             var ar = this.el.select('label > span',true);
11259             
11260             if (ar.elements.length) {
11261                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11262                 this.fieldLabel = v;
11263                 return;
11264             }
11265             
11266             var br = this.el.select('label',true);
11267             
11268             if(br.elements.length) {
11269                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11270                 this.fieldLabel = v;
11271                 return;
11272             }
11273             
11274             Roo.log('Cannot Found any of label > span || label in input');
11275             return;
11276         }
11277         
11278         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11279         this.fieldLabel = v;
11280         
11281         
11282     }
11283 });
11284
11285  
11286 /*
11287  * - LGPL
11288  *
11289  * Input
11290  * 
11291  */
11292
11293 /**
11294  * @class Roo.bootstrap.TextArea
11295  * @extends Roo.bootstrap.Input
11296  * Bootstrap TextArea class
11297  * @cfg {Number} cols Specifies the visible width of a text area
11298  * @cfg {Number} rows Specifies the visible number of lines in a text area
11299  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11300  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11301  * @cfg {string} html text
11302  * 
11303  * @constructor
11304  * Create a new TextArea
11305  * @param {Object} config The config object
11306  */
11307
11308 Roo.bootstrap.TextArea = function(config){
11309     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11310    
11311 };
11312
11313 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11314      
11315     cols : false,
11316     rows : 5,
11317     readOnly : false,
11318     warp : 'soft',
11319     resize : false,
11320     value: false,
11321     html: false,
11322     
11323     getAutoCreate : function(){
11324         
11325         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11326         
11327         var id = Roo.id();
11328         
11329         var cfg = {};
11330         
11331         if(this.inputType != 'hidden'){
11332             cfg.cls = 'form-group' //input-group
11333         }
11334         
11335         var input =  {
11336             tag: 'textarea',
11337             id : id,
11338             warp : this.warp,
11339             rows : this.rows,
11340             value : this.value || '',
11341             html: this.html || '',
11342             cls : 'form-control',
11343             placeholder : this.placeholder || '' 
11344             
11345         };
11346         
11347         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11348             input.maxLength = this.maxLength;
11349         }
11350         
11351         if(this.resize){
11352             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11353         }
11354         
11355         if(this.cols){
11356             input.cols = this.cols;
11357         }
11358         
11359         if (this.readOnly) {
11360             input.readonly = true;
11361         }
11362         
11363         if (this.name) {
11364             input.name = this.name;
11365         }
11366         
11367         if (this.size) {
11368             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11369         }
11370         
11371         var settings=this;
11372         ['xs','sm','md','lg'].map(function(size){
11373             if (settings[size]) {
11374                 cfg.cls += ' col-' + size + '-' + settings[size];
11375             }
11376         });
11377         
11378         var inputblock = input;
11379         
11380         if(this.hasFeedback && !this.allowBlank){
11381             
11382             var feedback = {
11383                 tag: 'span',
11384                 cls: 'glyphicon form-control-feedback'
11385             };
11386
11387             inputblock = {
11388                 cls : 'has-feedback',
11389                 cn :  [
11390                     input,
11391                     feedback
11392                 ] 
11393             };  
11394         }
11395         
11396         
11397         if (this.before || this.after) {
11398             
11399             inputblock = {
11400                 cls : 'input-group',
11401                 cn :  [] 
11402             };
11403             if (this.before) {
11404                 inputblock.cn.push({
11405                     tag :'span',
11406                     cls : 'input-group-addon',
11407                     html : this.before
11408                 });
11409             }
11410             
11411             inputblock.cn.push(input);
11412             
11413             if(this.hasFeedback && !this.allowBlank){
11414                 inputblock.cls += ' has-feedback';
11415                 inputblock.cn.push(feedback);
11416             }
11417             
11418             if (this.after) {
11419                 inputblock.cn.push({
11420                     tag :'span',
11421                     cls : 'input-group-addon',
11422                     html : this.after
11423                 });
11424             }
11425             
11426         }
11427         
11428         if (align ==='left' && this.fieldLabel.length) {
11429             cfg.cn = [
11430                 {
11431                     tag: 'label',
11432                     'for' :  id,
11433                     cls : 'control-label',
11434                     html : this.fieldLabel
11435                 },
11436                 {
11437                     cls : "",
11438                     cn: [
11439                         inputblock
11440                     ]
11441                 }
11442
11443             ];
11444             
11445             if(this.labelWidth > 12){
11446                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11447             }
11448
11449             if(this.labelWidth < 13 && this.labelmd == 0){
11450                 this.labelmd = this.labelWidth;
11451             }
11452
11453             if(this.labellg > 0){
11454                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11455                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11456             }
11457
11458             if(this.labelmd > 0){
11459                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11460                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11461             }
11462
11463             if(this.labelsm > 0){
11464                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11465                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11466             }
11467
11468             if(this.labelxs > 0){
11469                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11470                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11471             }
11472             
11473         } else if ( this.fieldLabel.length) {
11474             cfg.cn = [
11475
11476                {
11477                    tag: 'label',
11478                    //cls : 'input-group-addon',
11479                    html : this.fieldLabel
11480
11481                },
11482
11483                inputblock
11484
11485            ];
11486
11487         } else {
11488
11489             cfg.cn = [
11490
11491                 inputblock
11492
11493             ];
11494                 
11495         }
11496         
11497         if (this.disabled) {
11498             input.disabled=true;
11499         }
11500         
11501         return cfg;
11502         
11503     },
11504     /**
11505      * return the real textarea element.
11506      */
11507     inputEl: function ()
11508     {
11509         return this.el.select('textarea.form-control',true).first();
11510     },
11511     
11512     /**
11513      * Clear any invalid styles/messages for this field
11514      */
11515     clearInvalid : function()
11516     {
11517         
11518         if(!this.el || this.preventMark){ // not rendered
11519             return;
11520         }
11521         
11522         var label = this.el.select('label', true).first();
11523         var icon = this.el.select('i.fa-star', true).first();
11524         
11525         if(label && icon){
11526             icon.remove();
11527         }
11528         this.el.removeClass( this.validClass);
11529         this.inputEl().removeClass('is-invalid');
11530          
11531         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11532             
11533             var feedback = this.el.select('.form-control-feedback', true).first();
11534             
11535             if(feedback){
11536                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11537             }
11538             
11539         }
11540         
11541         this.fireEvent('valid', this);
11542     },
11543     
11544      /**
11545      * Mark this field as valid
11546      */
11547     markValid : function()
11548     {
11549         if(!this.el  || this.preventMark){ // not rendered
11550             return;
11551         }
11552         
11553         this.el.removeClass([this.invalidClass, this.validClass]);
11554         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11555         
11556         var feedback = this.el.select('.form-control-feedback', true).first();
11557             
11558         if(feedback){
11559             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11560         }
11561
11562         if(this.disabled || this.allowBlank){
11563             return;
11564         }
11565         
11566         var label = this.el.select('label', true).first();
11567         var icon = this.el.select('i.fa-star', true).first();
11568         
11569         if(label && icon){
11570             icon.remove();
11571         }
11572         if (Roo.bootstrap.version == 3) {
11573             this.el.addClass(this.validClass);
11574         } else {
11575             this.inputEl().addClass('is-valid');
11576         }
11577         
11578         
11579         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11580             
11581             var feedback = this.el.select('.form-control-feedback', true).first();
11582             
11583             if(feedback){
11584                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11585                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11586             }
11587             
11588         }
11589         
11590         this.fireEvent('valid', this);
11591     },
11592     
11593      /**
11594      * Mark this field as invalid
11595      * @param {String} msg The validation message
11596      */
11597     markInvalid : function(msg)
11598     {
11599         if(!this.el  || this.preventMark){ // not rendered
11600             return;
11601         }
11602         
11603         this.el.removeClass([this.invalidClass, this.validClass]);
11604         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11605         
11606         var feedback = this.el.select('.form-control-feedback', true).first();
11607             
11608         if(feedback){
11609             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11610         }
11611
11612         if(this.disabled || this.allowBlank){
11613             return;
11614         }
11615         
11616         var label = this.el.select('label', true).first();
11617         var icon = this.el.select('i.fa-star', true).first();
11618         
11619         if(!this.getValue().length && label && !icon){
11620             this.el.createChild({
11621                 tag : 'i',
11622                 cls : 'text-danger fa fa-lg fa-star',
11623                 tooltip : 'This field is required',
11624                 style : 'margin-right:5px;'
11625             }, label, true);
11626         }
11627         
11628         if (Roo.bootstrap.version == 3) {
11629             this.el.addClass(this.invalidClass);
11630         } else {
11631             this.inputEl().addClass('is-invalid');
11632         }
11633         
11634         // fixme ... this may be depricated need to test..
11635         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11636             
11637             var feedback = this.el.select('.form-control-feedback', true).first();
11638             
11639             if(feedback){
11640                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11641                 
11642                 if(this.getValue().length || this.forceFeedback){
11643                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11644                 }
11645                 
11646             }
11647             
11648         }
11649         
11650         this.fireEvent('invalid', this, msg);
11651     }
11652 });
11653
11654  
11655 /*
11656  * - LGPL
11657  *
11658  * trigger field - base class for combo..
11659  * 
11660  */
11661  
11662 /**
11663  * @class Roo.bootstrap.TriggerField
11664  * @extends Roo.bootstrap.Input
11665  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11666  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11667  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11668  * for which you can provide a custom implementation.  For example:
11669  * <pre><code>
11670 var trigger = new Roo.bootstrap.TriggerField();
11671 trigger.onTriggerClick = myTriggerFn;
11672 trigger.applyTo('my-field');
11673 </code></pre>
11674  *
11675  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11676  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11677  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11678  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11679  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11680
11681  * @constructor
11682  * Create a new TriggerField.
11683  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11684  * to the base TextField)
11685  */
11686 Roo.bootstrap.TriggerField = function(config){
11687     this.mimicing = false;
11688     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11689 };
11690
11691 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11692     /**
11693      * @cfg {String} triggerClass A CSS class to apply to the trigger
11694      */
11695      /**
11696      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11697      */
11698     hideTrigger:false,
11699
11700     /**
11701      * @cfg {Boolean} removable (true|false) special filter default false
11702      */
11703     removable : false,
11704     
11705     /** @cfg {Boolean} grow @hide */
11706     /** @cfg {Number} growMin @hide */
11707     /** @cfg {Number} growMax @hide */
11708
11709     /**
11710      * @hide 
11711      * @method
11712      */
11713     autoSize: Roo.emptyFn,
11714     // private
11715     monitorTab : true,
11716     // private
11717     deferHeight : true,
11718
11719     
11720     actionMode : 'wrap',
11721     
11722     caret : false,
11723     
11724     
11725     getAutoCreate : function(){
11726        
11727         var align = this.labelAlign || this.parentLabelAlign();
11728         
11729         var id = Roo.id();
11730         
11731         var cfg = {
11732             cls: 'form-group' //input-group
11733         };
11734         
11735         
11736         var input =  {
11737             tag: 'input',
11738             id : id,
11739             type : this.inputType,
11740             cls : 'form-control',
11741             autocomplete: 'new-password',
11742             placeholder : this.placeholder || '' 
11743             
11744         };
11745         if (this.name) {
11746             input.name = this.name;
11747         }
11748         if (this.size) {
11749             input.cls += ' input-' + this.size;
11750         }
11751         
11752         if (this.disabled) {
11753             input.disabled=true;
11754         }
11755         
11756         var inputblock = input;
11757         
11758         if(this.hasFeedback && !this.allowBlank){
11759             
11760             var feedback = {
11761                 tag: 'span',
11762                 cls: 'glyphicon form-control-feedback'
11763             };
11764             
11765             if(this.removable && !this.editable  ){
11766                 inputblock = {
11767                     cls : 'has-feedback',
11768                     cn :  [
11769                         inputblock,
11770                         {
11771                             tag: 'button',
11772                             html : 'x',
11773                             cls : 'roo-combo-removable-btn close'
11774                         },
11775                         feedback
11776                     ] 
11777                 };
11778             } else {
11779                 inputblock = {
11780                     cls : 'has-feedback',
11781                     cn :  [
11782                         inputblock,
11783                         feedback
11784                     ] 
11785                 };
11786             }
11787
11788         } else {
11789             if(this.removable && !this.editable ){
11790                 inputblock = {
11791                     cls : 'roo-removable',
11792                     cn :  [
11793                         inputblock,
11794                         {
11795                             tag: 'button',
11796                             html : 'x',
11797                             cls : 'roo-combo-removable-btn close'
11798                         }
11799                     ] 
11800                 };
11801             }
11802         }
11803         
11804         if (this.before || this.after) {
11805             
11806             inputblock = {
11807                 cls : 'input-group',
11808                 cn :  [] 
11809             };
11810             if (this.before) {
11811                 inputblock.cn.push({
11812                     tag :'span',
11813                     cls : 'input-group-addon input-group-prepend input-group-text',
11814                     html : this.before
11815                 });
11816             }
11817             
11818             inputblock.cn.push(input);
11819             
11820             if(this.hasFeedback && !this.allowBlank){
11821                 inputblock.cls += ' has-feedback';
11822                 inputblock.cn.push(feedback);
11823             }
11824             
11825             if (this.after) {
11826                 inputblock.cn.push({
11827                     tag :'span',
11828                     cls : 'input-group-addon input-group-append input-group-text',
11829                     html : this.after
11830                 });
11831             }
11832             
11833         };
11834         
11835       
11836         
11837         var ibwrap = inputblock;
11838         
11839         if(this.multiple){
11840             ibwrap = {
11841                 tag: 'ul',
11842                 cls: 'roo-select2-choices',
11843                 cn:[
11844                     {
11845                         tag: 'li',
11846                         cls: 'roo-select2-search-field',
11847                         cn: [
11848
11849                             inputblock
11850                         ]
11851                     }
11852                 ]
11853             };
11854                 
11855         }
11856         
11857         var combobox = {
11858             cls: 'roo-select2-container input-group',
11859             cn: [
11860                  {
11861                     tag: 'input',
11862                     type : 'hidden',
11863                     cls: 'form-hidden-field'
11864                 },
11865                 ibwrap
11866             ]
11867         };
11868         
11869         if(!this.multiple && this.showToggleBtn){
11870             
11871             var caret = {
11872                         tag: 'span',
11873                         cls: 'caret'
11874              };
11875             if (this.caret != false) {
11876                 caret = {
11877                      tag: 'i',
11878                      cls: 'fa fa-' + this.caret
11879                 };
11880                 
11881             }
11882             
11883             combobox.cn.push({
11884                 tag :'span',
11885                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11886                 cn : [
11887                     Roo.bootstrap.version == 3 ? caret : '',
11888                     {
11889                         tag: 'span',
11890                         cls: 'combobox-clear',
11891                         cn  : [
11892                             {
11893                                 tag : 'i',
11894                                 cls: 'icon-remove'
11895                             }
11896                         ]
11897                     }
11898                 ]
11899
11900             })
11901         }
11902         
11903         if(this.multiple){
11904             combobox.cls += ' roo-select2-container-multi';
11905         }
11906          var indicator = {
11907             tag : 'i',
11908             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11909             tooltip : 'This field is required'
11910         };
11911         if (Roo.bootstrap.version == 4) {
11912             indicator = {
11913                 tag : 'i',
11914                 style : 'display:none'
11915             };
11916         }
11917         
11918         
11919         if (align ==='left' && this.fieldLabel.length) {
11920             
11921             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11922
11923             cfg.cn = [
11924                 indicator,
11925                 {
11926                     tag: 'label',
11927                     'for' :  id,
11928                     cls : 'control-label',
11929                     html : this.fieldLabel
11930
11931                 },
11932                 {
11933                     cls : "", 
11934                     cn: [
11935                         combobox
11936                     ]
11937                 }
11938
11939             ];
11940             
11941             var labelCfg = cfg.cn[1];
11942             var contentCfg = cfg.cn[2];
11943             
11944             if(this.indicatorpos == 'right'){
11945                 cfg.cn = [
11946                     {
11947                         tag: 'label',
11948                         'for' :  id,
11949                         cls : 'control-label',
11950                         cn : [
11951                             {
11952                                 tag : 'span',
11953                                 html : this.fieldLabel
11954                             },
11955                             indicator
11956                         ]
11957                     },
11958                     {
11959                         cls : "", 
11960                         cn: [
11961                             combobox
11962                         ]
11963                     }
11964
11965                 ];
11966                 
11967                 labelCfg = cfg.cn[0];
11968                 contentCfg = cfg.cn[1];
11969             }
11970             
11971             if(this.labelWidth > 12){
11972                 labelCfg.style = "width: " + this.labelWidth + 'px';
11973             }
11974             
11975             if(this.labelWidth < 13 && this.labelmd == 0){
11976                 this.labelmd = this.labelWidth;
11977             }
11978             
11979             if(this.labellg > 0){
11980                 labelCfg.cls += ' col-lg-' + this.labellg;
11981                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11982             }
11983             
11984             if(this.labelmd > 0){
11985                 labelCfg.cls += ' col-md-' + this.labelmd;
11986                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11987             }
11988             
11989             if(this.labelsm > 0){
11990                 labelCfg.cls += ' col-sm-' + this.labelsm;
11991                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11992             }
11993             
11994             if(this.labelxs > 0){
11995                 labelCfg.cls += ' col-xs-' + this.labelxs;
11996                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11997             }
11998             
11999         } else if ( this.fieldLabel.length) {
12000 //                Roo.log(" label");
12001             cfg.cn = [
12002                 indicator,
12003                {
12004                    tag: 'label',
12005                    //cls : 'input-group-addon',
12006                    html : this.fieldLabel
12007
12008                },
12009
12010                combobox
12011
12012             ];
12013             
12014             if(this.indicatorpos == 'right'){
12015                 
12016                 cfg.cn = [
12017                     {
12018                        tag: 'label',
12019                        cn : [
12020                            {
12021                                tag : 'span',
12022                                html : this.fieldLabel
12023                            },
12024                            indicator
12025                        ]
12026
12027                     },
12028                     combobox
12029
12030                 ];
12031
12032             }
12033
12034         } else {
12035             
12036 //                Roo.log(" no label && no align");
12037                 cfg = combobox
12038                      
12039                 
12040         }
12041         
12042         var settings=this;
12043         ['xs','sm','md','lg'].map(function(size){
12044             if (settings[size]) {
12045                 cfg.cls += ' col-' + size + '-' + settings[size];
12046             }
12047         });
12048         
12049         return cfg;
12050         
12051     },
12052     
12053     
12054     
12055     // private
12056     onResize : function(w, h){
12057 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12058 //        if(typeof w == 'number'){
12059 //            var x = w - this.trigger.getWidth();
12060 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12061 //            this.trigger.setStyle('left', x+'px');
12062 //        }
12063     },
12064
12065     // private
12066     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12067
12068     // private
12069     getResizeEl : function(){
12070         return this.inputEl();
12071     },
12072
12073     // private
12074     getPositionEl : function(){
12075         return this.inputEl();
12076     },
12077
12078     // private
12079     alignErrorIcon : function(){
12080         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12081     },
12082
12083     // private
12084     initEvents : function(){
12085         
12086         this.createList();
12087         
12088         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12089         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12090         if(!this.multiple && this.showToggleBtn){
12091             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12092             if(this.hideTrigger){
12093                 this.trigger.setDisplayed(false);
12094             }
12095             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12096         }
12097         
12098         if(this.multiple){
12099             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12100         }
12101         
12102         if(this.removable && !this.editable && !this.tickable){
12103             var close = this.closeTriggerEl();
12104             
12105             if(close){
12106                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12107                 close.on('click', this.removeBtnClick, this, close);
12108             }
12109         }
12110         
12111         //this.trigger.addClassOnOver('x-form-trigger-over');
12112         //this.trigger.addClassOnClick('x-form-trigger-click');
12113         
12114         //if(!this.width){
12115         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12116         //}
12117     },
12118     
12119     closeTriggerEl : function()
12120     {
12121         var close = this.el.select('.roo-combo-removable-btn', true).first();
12122         return close ? close : false;
12123     },
12124     
12125     removeBtnClick : function(e, h, el)
12126     {
12127         e.preventDefault();
12128         
12129         if(this.fireEvent("remove", this) !== false){
12130             this.reset();
12131             this.fireEvent("afterremove", this)
12132         }
12133     },
12134     
12135     createList : function()
12136     {
12137         this.list = Roo.get(document.body).createChild({
12138             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12139             cls: 'typeahead typeahead-long dropdown-menu',
12140             style: 'display:none'
12141         });
12142         
12143         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12144         
12145     },
12146
12147     // private
12148     initTrigger : function(){
12149        
12150     },
12151
12152     // private
12153     onDestroy : function(){
12154         if(this.trigger){
12155             this.trigger.removeAllListeners();
12156           //  this.trigger.remove();
12157         }
12158         //if(this.wrap){
12159         //    this.wrap.remove();
12160         //}
12161         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12162     },
12163
12164     // private
12165     onFocus : function(){
12166         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12167         /*
12168         if(!this.mimicing){
12169             this.wrap.addClass('x-trigger-wrap-focus');
12170             this.mimicing = true;
12171             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12172             if(this.monitorTab){
12173                 this.el.on("keydown", this.checkTab, this);
12174             }
12175         }
12176         */
12177     },
12178
12179     // private
12180     checkTab : function(e){
12181         if(e.getKey() == e.TAB){
12182             this.triggerBlur();
12183         }
12184     },
12185
12186     // private
12187     onBlur : function(){
12188         // do nothing
12189     },
12190
12191     // private
12192     mimicBlur : function(e, t){
12193         /*
12194         if(!this.wrap.contains(t) && this.validateBlur()){
12195             this.triggerBlur();
12196         }
12197         */
12198     },
12199
12200     // private
12201     triggerBlur : function(){
12202         this.mimicing = false;
12203         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12204         if(this.monitorTab){
12205             this.el.un("keydown", this.checkTab, this);
12206         }
12207         //this.wrap.removeClass('x-trigger-wrap-focus');
12208         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12209     },
12210
12211     // private
12212     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12213     validateBlur : function(e, t){
12214         return true;
12215     },
12216
12217     // private
12218     onDisable : function(){
12219         this.inputEl().dom.disabled = true;
12220         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12221         //if(this.wrap){
12222         //    this.wrap.addClass('x-item-disabled');
12223         //}
12224     },
12225
12226     // private
12227     onEnable : function(){
12228         this.inputEl().dom.disabled = false;
12229         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12230         //if(this.wrap){
12231         //    this.el.removeClass('x-item-disabled');
12232         //}
12233     },
12234
12235     // private
12236     onShow : function(){
12237         var ae = this.getActionEl();
12238         
12239         if(ae){
12240             ae.dom.style.display = '';
12241             ae.dom.style.visibility = 'visible';
12242         }
12243     },
12244
12245     // private
12246     
12247     onHide : function(){
12248         var ae = this.getActionEl();
12249         ae.dom.style.display = 'none';
12250     },
12251
12252     /**
12253      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12254      * by an implementing function.
12255      * @method
12256      * @param {EventObject} e
12257      */
12258     onTriggerClick : Roo.emptyFn
12259 });
12260  
12261 /*
12262 * Licence: LGPL
12263 */
12264
12265 /**
12266  * @class Roo.bootstrap.CardUploader
12267  * @extends Roo.bootstrap.Button
12268  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12269  * @cfg {Number} errorTimeout default 3000
12270  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12271  * @cfg {Array}  html The button text.
12272
12273  *
12274  * @constructor
12275  * Create a new CardUploader
12276  * @param {Object} config The config object
12277  */
12278
12279 Roo.bootstrap.CardUploader = function(config){
12280     
12281  
12282     
12283     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12284     
12285     
12286     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12287         return r.data.id
12288         });
12289     
12290     
12291 };
12292
12293 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12294     
12295      
12296     errorTimeout : 3000,
12297      
12298     images : false,
12299    
12300     fileCollection : false,
12301     allowBlank : true,
12302     
12303     getAutoCreate : function()
12304     {
12305         
12306         var cfg =  {
12307             cls :'form-group' ,
12308             cn : [
12309                
12310                 {
12311                     tag: 'label',
12312                    //cls : 'input-group-addon',
12313                     html : this.fieldLabel
12314
12315                 },
12316
12317                 {
12318                     tag: 'input',
12319                     type : 'hidden',
12320                     value : this.value,
12321                     cls : 'd-none  form-control'
12322                 },
12323                 
12324                 {
12325                     tag: 'input',
12326                     multiple : 'multiple',
12327                     type : 'file',
12328                     cls : 'd-none  roo-card-upload-selector'
12329                 },
12330                 
12331                 {
12332                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12333                 },
12334                 {
12335                     cls : 'card-columns roo-card-uploader-container'
12336                 }
12337
12338             ]
12339         };
12340            
12341          
12342         return cfg;
12343     },
12344     
12345     getChildContainer : function() /// what children are added to.
12346     {
12347         return this.containerEl;
12348     },
12349    
12350     getButtonContainer : function() /// what children are added to.
12351     {
12352         return this.el.select(".roo-card-uploader-button-container").first();
12353     },
12354    
12355     initEvents : function()
12356     {
12357         
12358         Roo.bootstrap.Input.prototype.initEvents.call(this);
12359         
12360         var t = this;
12361         this.addxtype({
12362             xns: Roo.bootstrap,
12363
12364             xtype : 'Button',
12365             container_method : 'getButtonContainer' ,            
12366             html :  this.html, // fix changable?
12367             cls : 'w-100 ',
12368             listeners : {
12369                 'click' : function(btn, e) {
12370                     t.onClick(e);
12371                 }
12372             }
12373         });
12374         
12375         
12376         
12377         
12378         this.urlAPI = (window.createObjectURL && window) || 
12379                                 (window.URL && URL.revokeObjectURL && URL) || 
12380                                 (window.webkitURL && webkitURL);
12381                         
12382          
12383          
12384          
12385         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12386         
12387         this.selectorEl.on('change', this.onFileSelected, this);
12388         if (this.images) {
12389             var t = this;
12390             this.images.forEach(function(img) {
12391                 t.addCard(img)
12392             });
12393             this.images = false;
12394         }
12395         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12396          
12397        
12398     },
12399     
12400    
12401     onClick : function(e)
12402     {
12403         e.preventDefault();
12404          
12405         this.selectorEl.dom.click();
12406          
12407     },
12408     
12409     onFileSelected : function(e)
12410     {
12411         e.preventDefault();
12412         
12413         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12414             return;
12415         }
12416         
12417         Roo.each(this.selectorEl.dom.files, function(file){    
12418             this.addFile(file);
12419         }, this);
12420          
12421     },
12422     
12423       
12424     
12425       
12426     
12427     addFile : function(file)
12428     {
12429            
12430         if(typeof(file) === 'string'){
12431             throw "Add file by name?"; // should not happen
12432             return;
12433         }
12434         
12435         if(!file || !this.urlAPI){
12436             return;
12437         }
12438         
12439         // file;
12440         // file.type;
12441         
12442         var _this = this;
12443         
12444         
12445         var url = _this.urlAPI.createObjectURL( file);
12446            
12447         this.addCard({
12448             id : Roo.bootstrap.CardUploader.ID--,
12449             is_uploaded : false,
12450             src : url,
12451             title : file.name,
12452             mimetype : file.type,
12453             preview : false,
12454             is_deleted : 0
12455         })
12456         
12457     },
12458     
12459     addCard : function (data)
12460     {
12461         // hidden input element?
12462         // if the file is not an image...
12463         //then we need to use something other that and header_image
12464         var t = this;
12465         //   remove.....
12466         var footer = [
12467             {
12468                 xns : Roo.bootstrap,
12469                 xtype : 'CardFooter',
12470                 items: [
12471                     {
12472                         xns : Roo.bootstrap,
12473                         xtype : 'Element',
12474                         cls : 'd-flex',
12475                         items : [
12476                             
12477                             {
12478                                 xns : Roo.bootstrap,
12479                                 xtype : 'Button',
12480                                 html : String.format("<small>{0}</small>", data.title),
12481                                 cls : 'col-11 text-left',
12482                                 size: 'sm',
12483                                 weight: 'link',
12484                                 fa : 'download',
12485                                 listeners : {
12486                                     click : function() {
12487                                         this.downloadCard(data.id)
12488                                     }
12489                                 }
12490                             },
12491                           
12492                             {
12493                                 xns : Roo.bootstrap,
12494                                 xtype : 'Button',
12495                                 
12496                                 size : 'sm',
12497                                 weight: 'danger',
12498                                 cls : 'col-1',
12499                                 fa : 'times',
12500                                 listeners : {
12501                                     click : function() {
12502                                         t.removeCard(data.id)
12503                                     }
12504                                 }
12505                             }
12506                         ]
12507                     }
12508                     
12509                 ] 
12510             }
12511             
12512         ];
12513
12514         var cn = this.addxtype(
12515             {
12516                  
12517                 xns : Roo.bootstrap,
12518                 xtype : 'Card',
12519                 closeable : true,
12520                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12521                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12522                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12523                 data : data,
12524                 html : false,
12525                  
12526                 items : footer,
12527                 initEvents : function() {
12528                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12529                     this.imgEl = this.el.select('.card-img-top').first();
12530                     if (this.imgEl) {
12531                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12532                         this.imgEl.set({ 'pointer' : 'cursor' });
12533                                   
12534                     }
12535                     
12536                   
12537                 }
12538                 
12539             }
12540         );
12541         // dont' really need ot update items.
12542         // this.items.push(cn);
12543         this.fileCollection.add(cn);
12544         this.updateInput();
12545         
12546     },
12547     removeCard : function(id)
12548     {
12549         
12550         var card  = this.fileCollection.get(id);
12551         card.data.is_deleted = 1;
12552         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12553         this.fileCollection.remove(card);
12554         //this.items = this.items.filter(function(e) { return e != card });
12555         // dont' really need ot update items.
12556         card.el.dom.parentNode.removeChild(card.el.dom);
12557         
12558     },
12559     reset: function()
12560     {
12561         this.fileCollection.each(function(card) {
12562             card.el.dom.parentNode.removeChild(card.el.dom);    
12563         });
12564         this.fileCollection.clear();
12565         this.updateInput();
12566     },
12567     
12568     updateInput : function()
12569     {
12570         var data = [];
12571         this.fileCollection.each(function(e) {
12572             data.push(e.data);
12573         });
12574         
12575         this.inputEl().dom.value = JSON.stringify(data);
12576     }
12577     
12578     
12579 });
12580
12581
12582 Roo.bootstrap.CardUploader.ID = -1;/*
12583  * Based on:
12584  * Ext JS Library 1.1.1
12585  * Copyright(c) 2006-2007, Ext JS, LLC.
12586  *
12587  * Originally Released Under LGPL - original licence link has changed is not relivant.
12588  *
12589  * Fork - LGPL
12590  * <script type="text/javascript">
12591  */
12592
12593
12594 /**
12595  * @class Roo.data.SortTypes
12596  * @singleton
12597  * Defines the default sorting (casting?) comparison functions used when sorting data.
12598  */
12599 Roo.data.SortTypes = {
12600     /**
12601      * Default sort that does nothing
12602      * @param {Mixed} s The value being converted
12603      * @return {Mixed} The comparison value
12604      */
12605     none : function(s){
12606         return s;
12607     },
12608     
12609     /**
12610      * The regular expression used to strip tags
12611      * @type {RegExp}
12612      * @property
12613      */
12614     stripTagsRE : /<\/?[^>]+>/gi,
12615     
12616     /**
12617      * Strips all HTML tags to sort on text only
12618      * @param {Mixed} s The value being converted
12619      * @return {String} The comparison value
12620      */
12621     asText : function(s){
12622         return String(s).replace(this.stripTagsRE, "");
12623     },
12624     
12625     /**
12626      * Strips all HTML tags to sort on text only - Case insensitive
12627      * @param {Mixed} s The value being converted
12628      * @return {String} The comparison value
12629      */
12630     asUCText : function(s){
12631         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12632     },
12633     
12634     /**
12635      * Case insensitive string
12636      * @param {Mixed} s The value being converted
12637      * @return {String} The comparison value
12638      */
12639     asUCString : function(s) {
12640         return String(s).toUpperCase();
12641     },
12642     
12643     /**
12644      * Date sorting
12645      * @param {Mixed} s The value being converted
12646      * @return {Number} The comparison value
12647      */
12648     asDate : function(s) {
12649         if(!s){
12650             return 0;
12651         }
12652         if(s instanceof Date){
12653             return s.getTime();
12654         }
12655         return Date.parse(String(s));
12656     },
12657     
12658     /**
12659      * Float sorting
12660      * @param {Mixed} s The value being converted
12661      * @return {Float} The comparison value
12662      */
12663     asFloat : function(s) {
12664         var val = parseFloat(String(s).replace(/,/g, ""));
12665         if(isNaN(val)) {
12666             val = 0;
12667         }
12668         return val;
12669     },
12670     
12671     /**
12672      * Integer sorting
12673      * @param {Mixed} s The value being converted
12674      * @return {Number} The comparison value
12675      */
12676     asInt : function(s) {
12677         var val = parseInt(String(s).replace(/,/g, ""));
12678         if(isNaN(val)) {
12679             val = 0;
12680         }
12681         return val;
12682     }
12683 };/*
12684  * Based on:
12685  * Ext JS Library 1.1.1
12686  * Copyright(c) 2006-2007, Ext JS, LLC.
12687  *
12688  * Originally Released Under LGPL - original licence link has changed is not relivant.
12689  *
12690  * Fork - LGPL
12691  * <script type="text/javascript">
12692  */
12693
12694 /**
12695 * @class Roo.data.Record
12696  * Instances of this class encapsulate both record <em>definition</em> information, and record
12697  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12698  * to access Records cached in an {@link Roo.data.Store} object.<br>
12699  * <p>
12700  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12701  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12702  * objects.<br>
12703  * <p>
12704  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12705  * @constructor
12706  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12707  * {@link #create}. The parameters are the same.
12708  * @param {Array} data An associative Array of data values keyed by the field name.
12709  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12710  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12711  * not specified an integer id is generated.
12712  */
12713 Roo.data.Record = function(data, id){
12714     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12715     this.data = data;
12716 };
12717
12718 /**
12719  * Generate a constructor for a specific record layout.
12720  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12721  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12722  * Each field definition object may contain the following properties: <ul>
12723  * <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,
12724  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12725  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12726  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12727  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12728  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12729  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12730  * this may be omitted.</p></li>
12731  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12732  * <ul><li>auto (Default, implies no conversion)</li>
12733  * <li>string</li>
12734  * <li>int</li>
12735  * <li>float</li>
12736  * <li>boolean</li>
12737  * <li>date</li></ul></p></li>
12738  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12739  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12740  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12741  * by the Reader into an object that will be stored in the Record. It is passed the
12742  * following parameters:<ul>
12743  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12744  * </ul></p></li>
12745  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12746  * </ul>
12747  * <br>usage:<br><pre><code>
12748 var TopicRecord = Roo.data.Record.create(
12749     {name: 'title', mapping: 'topic_title'},
12750     {name: 'author', mapping: 'username'},
12751     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12752     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12753     {name: 'lastPoster', mapping: 'user2'},
12754     {name: 'excerpt', mapping: 'post_text'}
12755 );
12756
12757 var myNewRecord = new TopicRecord({
12758     title: 'Do my job please',
12759     author: 'noobie',
12760     totalPosts: 1,
12761     lastPost: new Date(),
12762     lastPoster: 'Animal',
12763     excerpt: 'No way dude!'
12764 });
12765 myStore.add(myNewRecord);
12766 </code></pre>
12767  * @method create
12768  * @static
12769  */
12770 Roo.data.Record.create = function(o){
12771     var f = function(){
12772         f.superclass.constructor.apply(this, arguments);
12773     };
12774     Roo.extend(f, Roo.data.Record);
12775     var p = f.prototype;
12776     p.fields = new Roo.util.MixedCollection(false, function(field){
12777         return field.name;
12778     });
12779     for(var i = 0, len = o.length; i < len; i++){
12780         p.fields.add(new Roo.data.Field(o[i]));
12781     }
12782     f.getField = function(name){
12783         return p.fields.get(name);  
12784     };
12785     return f;
12786 };
12787
12788 Roo.data.Record.AUTO_ID = 1000;
12789 Roo.data.Record.EDIT = 'edit';
12790 Roo.data.Record.REJECT = 'reject';
12791 Roo.data.Record.COMMIT = 'commit';
12792
12793 Roo.data.Record.prototype = {
12794     /**
12795      * Readonly flag - true if this record has been modified.
12796      * @type Boolean
12797      */
12798     dirty : false,
12799     editing : false,
12800     error: null,
12801     modified: null,
12802
12803     // private
12804     join : function(store){
12805         this.store = store;
12806     },
12807
12808     /**
12809      * Set the named field to the specified value.
12810      * @param {String} name The name of the field to set.
12811      * @param {Object} value The value to set the field to.
12812      */
12813     set : function(name, value){
12814         if(this.data[name] == value){
12815             return;
12816         }
12817         this.dirty = true;
12818         if(!this.modified){
12819             this.modified = {};
12820         }
12821         if(typeof this.modified[name] == 'undefined'){
12822             this.modified[name] = this.data[name];
12823         }
12824         this.data[name] = value;
12825         if(!this.editing && this.store){
12826             this.store.afterEdit(this);
12827         }       
12828     },
12829
12830     /**
12831      * Get the value of the named field.
12832      * @param {String} name The name of the field to get the value of.
12833      * @return {Object} The value of the field.
12834      */
12835     get : function(name){
12836         return this.data[name]; 
12837     },
12838
12839     // private
12840     beginEdit : function(){
12841         this.editing = true;
12842         this.modified = {}; 
12843     },
12844
12845     // private
12846     cancelEdit : function(){
12847         this.editing = false;
12848         delete this.modified;
12849     },
12850
12851     // private
12852     endEdit : function(){
12853         this.editing = false;
12854         if(this.dirty && this.store){
12855             this.store.afterEdit(this);
12856         }
12857     },
12858
12859     /**
12860      * Usually called by the {@link Roo.data.Store} which owns the Record.
12861      * Rejects all changes made to the Record since either creation, or the last commit operation.
12862      * Modified fields are reverted to their original values.
12863      * <p>
12864      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12865      * of reject operations.
12866      */
12867     reject : function(){
12868         var m = this.modified;
12869         for(var n in m){
12870             if(typeof m[n] != "function"){
12871                 this.data[n] = m[n];
12872             }
12873         }
12874         this.dirty = false;
12875         delete this.modified;
12876         this.editing = false;
12877         if(this.store){
12878             this.store.afterReject(this);
12879         }
12880     },
12881
12882     /**
12883      * Usually called by the {@link Roo.data.Store} which owns the Record.
12884      * Commits all changes made to the Record since either creation, or the last commit operation.
12885      * <p>
12886      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12887      * of commit operations.
12888      */
12889     commit : function(){
12890         this.dirty = false;
12891         delete this.modified;
12892         this.editing = false;
12893         if(this.store){
12894             this.store.afterCommit(this);
12895         }
12896     },
12897
12898     // private
12899     hasError : function(){
12900         return this.error != null;
12901     },
12902
12903     // private
12904     clearError : function(){
12905         this.error = null;
12906     },
12907
12908     /**
12909      * Creates a copy of this record.
12910      * @param {String} id (optional) A new record id if you don't want to use this record's id
12911      * @return {Record}
12912      */
12913     copy : function(newId) {
12914         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12915     }
12916 };/*
12917  * Based on:
12918  * Ext JS Library 1.1.1
12919  * Copyright(c) 2006-2007, Ext JS, LLC.
12920  *
12921  * Originally Released Under LGPL - original licence link has changed is not relivant.
12922  *
12923  * Fork - LGPL
12924  * <script type="text/javascript">
12925  */
12926
12927
12928
12929 /**
12930  * @class Roo.data.Store
12931  * @extends Roo.util.Observable
12932  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12933  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12934  * <p>
12935  * 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
12936  * has no knowledge of the format of the data returned by the Proxy.<br>
12937  * <p>
12938  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12939  * instances from the data object. These records are cached and made available through accessor functions.
12940  * @constructor
12941  * Creates a new Store.
12942  * @param {Object} config A config object containing the objects needed for the Store to access data,
12943  * and read the data into Records.
12944  */
12945 Roo.data.Store = function(config){
12946     this.data = new Roo.util.MixedCollection(false);
12947     this.data.getKey = function(o){
12948         return o.id;
12949     };
12950     this.baseParams = {};
12951     // private
12952     this.paramNames = {
12953         "start" : "start",
12954         "limit" : "limit",
12955         "sort" : "sort",
12956         "dir" : "dir",
12957         "multisort" : "_multisort"
12958     };
12959
12960     if(config && config.data){
12961         this.inlineData = config.data;
12962         delete config.data;
12963     }
12964
12965     Roo.apply(this, config);
12966     
12967     if(this.reader){ // reader passed
12968         this.reader = Roo.factory(this.reader, Roo.data);
12969         this.reader.xmodule = this.xmodule || false;
12970         if(!this.recordType){
12971             this.recordType = this.reader.recordType;
12972         }
12973         if(this.reader.onMetaChange){
12974             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12975         }
12976     }
12977
12978     if(this.recordType){
12979         this.fields = this.recordType.prototype.fields;
12980     }
12981     this.modified = [];
12982
12983     this.addEvents({
12984         /**
12985          * @event datachanged
12986          * Fires when the data cache has changed, and a widget which is using this Store
12987          * as a Record cache should refresh its view.
12988          * @param {Store} this
12989          */
12990         datachanged : true,
12991         /**
12992          * @event metachange
12993          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
12994          * @param {Store} this
12995          * @param {Object} meta The JSON metadata
12996          */
12997         metachange : true,
12998         /**
12999          * @event add
13000          * Fires when Records have been added to the Store
13001          * @param {Store} this
13002          * @param {Roo.data.Record[]} records The array of Records added
13003          * @param {Number} index The index at which the record(s) were added
13004          */
13005         add : true,
13006         /**
13007          * @event remove
13008          * Fires when a Record has been removed from the Store
13009          * @param {Store} this
13010          * @param {Roo.data.Record} record The Record that was removed
13011          * @param {Number} index The index at which the record was removed
13012          */
13013         remove : true,
13014         /**
13015          * @event update
13016          * Fires when a Record has been updated
13017          * @param {Store} this
13018          * @param {Roo.data.Record} record The Record that was updated
13019          * @param {String} operation The update operation being performed.  Value may be one of:
13020          * <pre><code>
13021  Roo.data.Record.EDIT
13022  Roo.data.Record.REJECT
13023  Roo.data.Record.COMMIT
13024          * </code></pre>
13025          */
13026         update : true,
13027         /**
13028          * @event clear
13029          * Fires when the data cache has been cleared.
13030          * @param {Store} this
13031          */
13032         clear : true,
13033         /**
13034          * @event beforeload
13035          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13036          * the load action will be canceled.
13037          * @param {Store} this
13038          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13039          */
13040         beforeload : true,
13041         /**
13042          * @event beforeloadadd
13043          * Fires after a new set of Records has been loaded.
13044          * @param {Store} this
13045          * @param {Roo.data.Record[]} records The Records that were loaded
13046          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13047          */
13048         beforeloadadd : true,
13049         /**
13050          * @event load
13051          * Fires after a new set of Records has been loaded, before they are added to the store.
13052          * @param {Store} this
13053          * @param {Roo.data.Record[]} records The Records that were loaded
13054          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13055          * @params {Object} return from reader
13056          */
13057         load : true,
13058         /**
13059          * @event loadexception
13060          * Fires if an exception occurs in the Proxy during loading.
13061          * Called with the signature of the Proxy's "loadexception" event.
13062          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13063          * 
13064          * @param {Proxy} 
13065          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13066          * @param {Object} load options 
13067          * @param {Object} jsonData from your request (normally this contains the Exception)
13068          */
13069         loadexception : true
13070     });
13071     
13072     if(this.proxy){
13073         this.proxy = Roo.factory(this.proxy, Roo.data);
13074         this.proxy.xmodule = this.xmodule || false;
13075         this.relayEvents(this.proxy,  ["loadexception"]);
13076     }
13077     this.sortToggle = {};
13078     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13079
13080     Roo.data.Store.superclass.constructor.call(this);
13081
13082     if(this.inlineData){
13083         this.loadData(this.inlineData);
13084         delete this.inlineData;
13085     }
13086 };
13087
13088 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13089      /**
13090     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13091     * without a remote query - used by combo/forms at present.
13092     */
13093     
13094     /**
13095     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13096     */
13097     /**
13098     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13099     */
13100     /**
13101     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13102     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13103     */
13104     /**
13105     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13106     * on any HTTP request
13107     */
13108     /**
13109     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13110     */
13111     /**
13112     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13113     */
13114     multiSort: false,
13115     /**
13116     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13117     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13118     */
13119     remoteSort : false,
13120
13121     /**
13122     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13123      * loaded or when a record is removed. (defaults to false).
13124     */
13125     pruneModifiedRecords : false,
13126
13127     // private
13128     lastOptions : null,
13129
13130     /**
13131      * Add Records to the Store and fires the add event.
13132      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13133      */
13134     add : function(records){
13135         records = [].concat(records);
13136         for(var i = 0, len = records.length; i < len; i++){
13137             records[i].join(this);
13138         }
13139         var index = this.data.length;
13140         this.data.addAll(records);
13141         this.fireEvent("add", this, records, index);
13142     },
13143
13144     /**
13145      * Remove a Record from the Store and fires the remove event.
13146      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13147      */
13148     remove : function(record){
13149         var index = this.data.indexOf(record);
13150         this.data.removeAt(index);
13151  
13152         if(this.pruneModifiedRecords){
13153             this.modified.remove(record);
13154         }
13155         this.fireEvent("remove", this, record, index);
13156     },
13157
13158     /**
13159      * Remove all Records from the Store and fires the clear event.
13160      */
13161     removeAll : function(){
13162         this.data.clear();
13163         if(this.pruneModifiedRecords){
13164             this.modified = [];
13165         }
13166         this.fireEvent("clear", this);
13167     },
13168
13169     /**
13170      * Inserts Records to the Store at the given index and fires the add event.
13171      * @param {Number} index The start index at which to insert the passed Records.
13172      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13173      */
13174     insert : function(index, records){
13175         records = [].concat(records);
13176         for(var i = 0, len = records.length; i < len; i++){
13177             this.data.insert(index, records[i]);
13178             records[i].join(this);
13179         }
13180         this.fireEvent("add", this, records, index);
13181     },
13182
13183     /**
13184      * Get the index within the cache of the passed Record.
13185      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13186      * @return {Number} The index of the passed Record. Returns -1 if not found.
13187      */
13188     indexOf : function(record){
13189         return this.data.indexOf(record);
13190     },
13191
13192     /**
13193      * Get the index within the cache of the Record with the passed id.
13194      * @param {String} id The id of the Record to find.
13195      * @return {Number} The index of the Record. Returns -1 if not found.
13196      */
13197     indexOfId : function(id){
13198         return this.data.indexOfKey(id);
13199     },
13200
13201     /**
13202      * Get the Record with the specified id.
13203      * @param {String} id The id of the Record to find.
13204      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13205      */
13206     getById : function(id){
13207         return this.data.key(id);
13208     },
13209
13210     /**
13211      * Get the Record at the specified index.
13212      * @param {Number} index The index of the Record to find.
13213      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13214      */
13215     getAt : function(index){
13216         return this.data.itemAt(index);
13217     },
13218
13219     /**
13220      * Returns a range of Records between specified indices.
13221      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13222      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13223      * @return {Roo.data.Record[]} An array of Records
13224      */
13225     getRange : function(start, end){
13226         return this.data.getRange(start, end);
13227     },
13228
13229     // private
13230     storeOptions : function(o){
13231         o = Roo.apply({}, o);
13232         delete o.callback;
13233         delete o.scope;
13234         this.lastOptions = o;
13235     },
13236
13237     /**
13238      * Loads the Record cache from the configured Proxy using the configured Reader.
13239      * <p>
13240      * If using remote paging, then the first load call must specify the <em>start</em>
13241      * and <em>limit</em> properties in the options.params property to establish the initial
13242      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13243      * <p>
13244      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13245      * and this call will return before the new data has been loaded. Perform any post-processing
13246      * in a callback function, or in a "load" event handler.</strong>
13247      * <p>
13248      * @param {Object} options An object containing properties which control loading options:<ul>
13249      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13250      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13251      * passed the following arguments:<ul>
13252      * <li>r : Roo.data.Record[]</li>
13253      * <li>options: Options object from the load call</li>
13254      * <li>success: Boolean success indicator</li></ul></li>
13255      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13256      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13257      * </ul>
13258      */
13259     load : function(options){
13260         options = options || {};
13261         if(this.fireEvent("beforeload", this, options) !== false){
13262             this.storeOptions(options);
13263             var p = Roo.apply(options.params || {}, this.baseParams);
13264             // if meta was not loaded from remote source.. try requesting it.
13265             if (!this.reader.metaFromRemote) {
13266                 p._requestMeta = 1;
13267             }
13268             if(this.sortInfo && this.remoteSort){
13269                 var pn = this.paramNames;
13270                 p[pn["sort"]] = this.sortInfo.field;
13271                 p[pn["dir"]] = this.sortInfo.direction;
13272             }
13273             if (this.multiSort) {
13274                 var pn = this.paramNames;
13275                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13276             }
13277             
13278             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13279         }
13280     },
13281
13282     /**
13283      * Reloads the Record cache from the configured Proxy using the configured Reader and
13284      * the options from the last load operation performed.
13285      * @param {Object} options (optional) An object containing properties which may override the options
13286      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13287      * the most recently used options are reused).
13288      */
13289     reload : function(options){
13290         this.load(Roo.applyIf(options||{}, this.lastOptions));
13291     },
13292
13293     // private
13294     // Called as a callback by the Reader during a load operation.
13295     loadRecords : function(o, options, success){
13296         if(!o || success === false){
13297             if(success !== false){
13298                 this.fireEvent("load", this, [], options, o);
13299             }
13300             if(options.callback){
13301                 options.callback.call(options.scope || this, [], options, false);
13302             }
13303             return;
13304         }
13305         // if data returned failure - throw an exception.
13306         if (o.success === false) {
13307             // show a message if no listener is registered.
13308             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13309                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13310             }
13311             // loadmask wil be hooked into this..
13312             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13313             return;
13314         }
13315         var r = o.records, t = o.totalRecords || r.length;
13316         
13317         this.fireEvent("beforeloadadd", this, r, options, o);
13318         
13319         if(!options || options.add !== true){
13320             if(this.pruneModifiedRecords){
13321                 this.modified = [];
13322             }
13323             for(var i = 0, len = r.length; i < len; i++){
13324                 r[i].join(this);
13325             }
13326             if(this.snapshot){
13327                 this.data = this.snapshot;
13328                 delete this.snapshot;
13329             }
13330             this.data.clear();
13331             this.data.addAll(r);
13332             this.totalLength = t;
13333             this.applySort();
13334             this.fireEvent("datachanged", this);
13335         }else{
13336             this.totalLength = Math.max(t, this.data.length+r.length);
13337             this.add(r);
13338         }
13339         
13340         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13341                 
13342             var e = new Roo.data.Record({});
13343
13344             e.set(this.parent.displayField, this.parent.emptyTitle);
13345             e.set(this.parent.valueField, '');
13346
13347             this.insert(0, e);
13348         }
13349             
13350         this.fireEvent("load", this, r, options, o);
13351         if(options.callback){
13352             options.callback.call(options.scope || this, r, options, true);
13353         }
13354     },
13355
13356
13357     /**
13358      * Loads data from a passed data block. A Reader which understands the format of the data
13359      * must have been configured in the constructor.
13360      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13361      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13362      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13363      */
13364     loadData : function(o, append){
13365         var r = this.reader.readRecords(o);
13366         this.loadRecords(r, {add: append}, true);
13367     },
13368     
13369      /**
13370      * using 'cn' the nested child reader read the child array into it's child stores.
13371      * @param {Object} rec The record with a 'children array
13372      */
13373     loadDataFromChildren : function(rec)
13374     {
13375         this.loadData(this.reader.toLoadData(rec));
13376     },
13377     
13378
13379     /**
13380      * Gets the number of cached records.
13381      * <p>
13382      * <em>If using paging, this may not be the total size of the dataset. If the data object
13383      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13384      * the data set size</em>
13385      */
13386     getCount : function(){
13387         return this.data.length || 0;
13388     },
13389
13390     /**
13391      * Gets the total number of records in the dataset as returned by the server.
13392      * <p>
13393      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13394      * the dataset size</em>
13395      */
13396     getTotalCount : function(){
13397         return this.totalLength || 0;
13398     },
13399
13400     /**
13401      * Returns the sort state of the Store as an object with two properties:
13402      * <pre><code>
13403  field {String} The name of the field by which the Records are sorted
13404  direction {String} The sort order, "ASC" or "DESC"
13405      * </code></pre>
13406      */
13407     getSortState : function(){
13408         return this.sortInfo;
13409     },
13410
13411     // private
13412     applySort : function(){
13413         if(this.sortInfo && !this.remoteSort){
13414             var s = this.sortInfo, f = s.field;
13415             var st = this.fields.get(f).sortType;
13416             var fn = function(r1, r2){
13417                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13418                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13419             };
13420             this.data.sort(s.direction, fn);
13421             if(this.snapshot && this.snapshot != this.data){
13422                 this.snapshot.sort(s.direction, fn);
13423             }
13424         }
13425     },
13426
13427     /**
13428      * Sets the default sort column and order to be used by the next load operation.
13429      * @param {String} fieldName The name of the field to sort by.
13430      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13431      */
13432     setDefaultSort : function(field, dir){
13433         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13434     },
13435
13436     /**
13437      * Sort the Records.
13438      * If remote sorting is used, the sort is performed on the server, and the cache is
13439      * reloaded. If local sorting is used, the cache is sorted internally.
13440      * @param {String} fieldName The name of the field to sort by.
13441      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13442      */
13443     sort : function(fieldName, dir){
13444         var f = this.fields.get(fieldName);
13445         if(!dir){
13446             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13447             
13448             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13449                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13450             }else{
13451                 dir = f.sortDir;
13452             }
13453         }
13454         this.sortToggle[f.name] = dir;
13455         this.sortInfo = {field: f.name, direction: dir};
13456         if(!this.remoteSort){
13457             this.applySort();
13458             this.fireEvent("datachanged", this);
13459         }else{
13460             this.load(this.lastOptions);
13461         }
13462     },
13463
13464     /**
13465      * Calls the specified function for each of the Records in the cache.
13466      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13467      * Returning <em>false</em> aborts and exits the iteration.
13468      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13469      */
13470     each : function(fn, scope){
13471         this.data.each(fn, scope);
13472     },
13473
13474     /**
13475      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13476      * (e.g., during paging).
13477      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13478      */
13479     getModifiedRecords : function(){
13480         return this.modified;
13481     },
13482
13483     // private
13484     createFilterFn : function(property, value, anyMatch){
13485         if(!value.exec){ // not a regex
13486             value = String(value);
13487             if(value.length == 0){
13488                 return false;
13489             }
13490             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13491         }
13492         return function(r){
13493             return value.test(r.data[property]);
13494         };
13495     },
13496
13497     /**
13498      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13499      * @param {String} property A field on your records
13500      * @param {Number} start The record index to start at (defaults to 0)
13501      * @param {Number} end The last record index to include (defaults to length - 1)
13502      * @return {Number} The sum
13503      */
13504     sum : function(property, start, end){
13505         var rs = this.data.items, v = 0;
13506         start = start || 0;
13507         end = (end || end === 0) ? end : rs.length-1;
13508
13509         for(var i = start; i <= end; i++){
13510             v += (rs[i].data[property] || 0);
13511         }
13512         return v;
13513     },
13514
13515     /**
13516      * Filter the records by a specified property.
13517      * @param {String} field A field on your records
13518      * @param {String/RegExp} value Either a string that the field
13519      * should start with or a RegExp to test against the field
13520      * @param {Boolean} anyMatch True to match any part not just the beginning
13521      */
13522     filter : function(property, value, anyMatch){
13523         var fn = this.createFilterFn(property, value, anyMatch);
13524         return fn ? this.filterBy(fn) : this.clearFilter();
13525     },
13526
13527     /**
13528      * Filter by a function. The specified function will be called with each
13529      * record in this data source. If the function returns true the record is included,
13530      * otherwise it is filtered.
13531      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13532      * @param {Object} scope (optional) The scope of the function (defaults to this)
13533      */
13534     filterBy : function(fn, scope){
13535         this.snapshot = this.snapshot || this.data;
13536         this.data = this.queryBy(fn, scope||this);
13537         this.fireEvent("datachanged", this);
13538     },
13539
13540     /**
13541      * Query the records by a specified property.
13542      * @param {String} field A field on your records
13543      * @param {String/RegExp} value Either a string that the field
13544      * should start with or a RegExp to test against the field
13545      * @param {Boolean} anyMatch True to match any part not just the beginning
13546      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13547      */
13548     query : function(property, value, anyMatch){
13549         var fn = this.createFilterFn(property, value, anyMatch);
13550         return fn ? this.queryBy(fn) : this.data.clone();
13551     },
13552
13553     /**
13554      * Query by a function. The specified function will be called with each
13555      * record in this data source. If the function returns true the record is included
13556      * in the results.
13557      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13558      * @param {Object} scope (optional) The scope of the function (defaults to this)
13559       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13560      **/
13561     queryBy : function(fn, scope){
13562         var data = this.snapshot || this.data;
13563         return data.filterBy(fn, scope||this);
13564     },
13565
13566     /**
13567      * Collects unique values for a particular dataIndex from this store.
13568      * @param {String} dataIndex The property to collect
13569      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13570      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13571      * @return {Array} An array of the unique values
13572      **/
13573     collect : function(dataIndex, allowNull, bypassFilter){
13574         var d = (bypassFilter === true && this.snapshot) ?
13575                 this.snapshot.items : this.data.items;
13576         var v, sv, r = [], l = {};
13577         for(var i = 0, len = d.length; i < len; i++){
13578             v = d[i].data[dataIndex];
13579             sv = String(v);
13580             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13581                 l[sv] = true;
13582                 r[r.length] = v;
13583             }
13584         }
13585         return r;
13586     },
13587
13588     /**
13589      * Revert to a view of the Record cache with no filtering applied.
13590      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13591      */
13592     clearFilter : function(suppressEvent){
13593         if(this.snapshot && this.snapshot != this.data){
13594             this.data = this.snapshot;
13595             delete this.snapshot;
13596             if(suppressEvent !== true){
13597                 this.fireEvent("datachanged", this);
13598             }
13599         }
13600     },
13601
13602     // private
13603     afterEdit : function(record){
13604         if(this.modified.indexOf(record) == -1){
13605             this.modified.push(record);
13606         }
13607         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13608     },
13609     
13610     // private
13611     afterReject : function(record){
13612         this.modified.remove(record);
13613         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13614     },
13615
13616     // private
13617     afterCommit : function(record){
13618         this.modified.remove(record);
13619         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13620     },
13621
13622     /**
13623      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13624      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13625      */
13626     commitChanges : function(){
13627         var m = this.modified.slice(0);
13628         this.modified = [];
13629         for(var i = 0, len = m.length; i < len; i++){
13630             m[i].commit();
13631         }
13632     },
13633
13634     /**
13635      * Cancel outstanding changes on all changed records.
13636      */
13637     rejectChanges : function(){
13638         var m = this.modified.slice(0);
13639         this.modified = [];
13640         for(var i = 0, len = m.length; i < len; i++){
13641             m[i].reject();
13642         }
13643     },
13644
13645     onMetaChange : function(meta, rtype, o){
13646         this.recordType = rtype;
13647         this.fields = rtype.prototype.fields;
13648         delete this.snapshot;
13649         this.sortInfo = meta.sortInfo || this.sortInfo;
13650         this.modified = [];
13651         this.fireEvent('metachange', this, this.reader.meta);
13652     },
13653     
13654     moveIndex : function(data, type)
13655     {
13656         var index = this.indexOf(data);
13657         
13658         var newIndex = index + type;
13659         
13660         this.remove(data);
13661         
13662         this.insert(newIndex, data);
13663         
13664     }
13665 });/*
13666  * Based on:
13667  * Ext JS Library 1.1.1
13668  * Copyright(c) 2006-2007, Ext JS, LLC.
13669  *
13670  * Originally Released Under LGPL - original licence link has changed is not relivant.
13671  *
13672  * Fork - LGPL
13673  * <script type="text/javascript">
13674  */
13675
13676 /**
13677  * @class Roo.data.SimpleStore
13678  * @extends Roo.data.Store
13679  * Small helper class to make creating Stores from Array data easier.
13680  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13681  * @cfg {Array} fields An array of field definition objects, or field name strings.
13682  * @cfg {Object} an existing reader (eg. copied from another store)
13683  * @cfg {Array} data The multi-dimensional array of data
13684  * @constructor
13685  * @param {Object} config
13686  */
13687 Roo.data.SimpleStore = function(config)
13688 {
13689     Roo.data.SimpleStore.superclass.constructor.call(this, {
13690         isLocal : true,
13691         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13692                 id: config.id
13693             },
13694             Roo.data.Record.create(config.fields)
13695         ),
13696         proxy : new Roo.data.MemoryProxy(config.data)
13697     });
13698     this.load();
13699 };
13700 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13701  * Based on:
13702  * Ext JS Library 1.1.1
13703  * Copyright(c) 2006-2007, Ext JS, LLC.
13704  *
13705  * Originally Released Under LGPL - original licence link has changed is not relivant.
13706  *
13707  * Fork - LGPL
13708  * <script type="text/javascript">
13709  */
13710
13711 /**
13712 /**
13713  * @extends Roo.data.Store
13714  * @class Roo.data.JsonStore
13715  * Small helper class to make creating Stores for JSON data easier. <br/>
13716 <pre><code>
13717 var store = new Roo.data.JsonStore({
13718     url: 'get-images.php',
13719     root: 'images',
13720     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13721 });
13722 </code></pre>
13723  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13724  * JsonReader and HttpProxy (unless inline data is provided).</b>
13725  * @cfg {Array} fields An array of field definition objects, or field name strings.
13726  * @constructor
13727  * @param {Object} config
13728  */
13729 Roo.data.JsonStore = function(c){
13730     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13731         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13732         reader: new Roo.data.JsonReader(c, c.fields)
13733     }));
13734 };
13735 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13736  * Based on:
13737  * Ext JS Library 1.1.1
13738  * Copyright(c) 2006-2007, Ext JS, LLC.
13739  *
13740  * Originally Released Under LGPL - original licence link has changed is not relivant.
13741  *
13742  * Fork - LGPL
13743  * <script type="text/javascript">
13744  */
13745
13746  
13747 Roo.data.Field = function(config){
13748     if(typeof config == "string"){
13749         config = {name: config};
13750     }
13751     Roo.apply(this, config);
13752     
13753     if(!this.type){
13754         this.type = "auto";
13755     }
13756     
13757     var st = Roo.data.SortTypes;
13758     // named sortTypes are supported, here we look them up
13759     if(typeof this.sortType == "string"){
13760         this.sortType = st[this.sortType];
13761     }
13762     
13763     // set default sortType for strings and dates
13764     if(!this.sortType){
13765         switch(this.type){
13766             case "string":
13767                 this.sortType = st.asUCString;
13768                 break;
13769             case "date":
13770                 this.sortType = st.asDate;
13771                 break;
13772             default:
13773                 this.sortType = st.none;
13774         }
13775     }
13776
13777     // define once
13778     var stripRe = /[\$,%]/g;
13779
13780     // prebuilt conversion function for this field, instead of
13781     // switching every time we're reading a value
13782     if(!this.convert){
13783         var cv, dateFormat = this.dateFormat;
13784         switch(this.type){
13785             case "":
13786             case "auto":
13787             case undefined:
13788                 cv = function(v){ return v; };
13789                 break;
13790             case "string":
13791                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13792                 break;
13793             case "int":
13794                 cv = function(v){
13795                     return v !== undefined && v !== null && v !== '' ?
13796                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13797                     };
13798                 break;
13799             case "float":
13800                 cv = function(v){
13801                     return v !== undefined && v !== null && v !== '' ?
13802                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13803                     };
13804                 break;
13805             case "bool":
13806             case "boolean":
13807                 cv = function(v){ return v === true || v === "true" || v == 1; };
13808                 break;
13809             case "date":
13810                 cv = function(v){
13811                     if(!v){
13812                         return '';
13813                     }
13814                     if(v instanceof Date){
13815                         return v;
13816                     }
13817                     if(dateFormat){
13818                         if(dateFormat == "timestamp"){
13819                             return new Date(v*1000);
13820                         }
13821                         return Date.parseDate(v, dateFormat);
13822                     }
13823                     var parsed = Date.parse(v);
13824                     return parsed ? new Date(parsed) : null;
13825                 };
13826              break;
13827             
13828         }
13829         this.convert = cv;
13830     }
13831 };
13832
13833 Roo.data.Field.prototype = {
13834     dateFormat: null,
13835     defaultValue: "",
13836     mapping: null,
13837     sortType : null,
13838     sortDir : "ASC"
13839 };/*
13840  * Based on:
13841  * Ext JS Library 1.1.1
13842  * Copyright(c) 2006-2007, Ext JS, LLC.
13843  *
13844  * Originally Released Under LGPL - original licence link has changed is not relivant.
13845  *
13846  * Fork - LGPL
13847  * <script type="text/javascript">
13848  */
13849  
13850 // Base class for reading structured data from a data source.  This class is intended to be
13851 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13852
13853 /**
13854  * @class Roo.data.DataReader
13855  * Base class for reading structured data from a data source.  This class is intended to be
13856  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13857  */
13858
13859 Roo.data.DataReader = function(meta, recordType){
13860     
13861     this.meta = meta;
13862     
13863     this.recordType = recordType instanceof Array ? 
13864         Roo.data.Record.create(recordType) : recordType;
13865 };
13866
13867 Roo.data.DataReader.prototype = {
13868     
13869     
13870     readerType : 'Data',
13871      /**
13872      * Create an empty record
13873      * @param {Object} data (optional) - overlay some values
13874      * @return {Roo.data.Record} record created.
13875      */
13876     newRow :  function(d) {
13877         var da =  {};
13878         this.recordType.prototype.fields.each(function(c) {
13879             switch( c.type) {
13880                 case 'int' : da[c.name] = 0; break;
13881                 case 'date' : da[c.name] = new Date(); break;
13882                 case 'float' : da[c.name] = 0.0; break;
13883                 case 'boolean' : da[c.name] = false; break;
13884                 default : da[c.name] = ""; break;
13885             }
13886             
13887         });
13888         return new this.recordType(Roo.apply(da, d));
13889     }
13890     
13891     
13892 };/*
13893  * Based on:
13894  * Ext JS Library 1.1.1
13895  * Copyright(c) 2006-2007, Ext JS, LLC.
13896  *
13897  * Originally Released Under LGPL - original licence link has changed is not relivant.
13898  *
13899  * Fork - LGPL
13900  * <script type="text/javascript">
13901  */
13902
13903 /**
13904  * @class Roo.data.DataProxy
13905  * @extends Roo.data.Observable
13906  * This class is an abstract base class for implementations which provide retrieval of
13907  * unformatted data objects.<br>
13908  * <p>
13909  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13910  * (of the appropriate type which knows how to parse the data object) to provide a block of
13911  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13912  * <p>
13913  * Custom implementations must implement the load method as described in
13914  * {@link Roo.data.HttpProxy#load}.
13915  */
13916 Roo.data.DataProxy = function(){
13917     this.addEvents({
13918         /**
13919          * @event beforeload
13920          * Fires before a network request is made to retrieve a data object.
13921          * @param {Object} This DataProxy object.
13922          * @param {Object} params The params parameter to the load function.
13923          */
13924         beforeload : true,
13925         /**
13926          * @event load
13927          * Fires before the load method's callback is called.
13928          * @param {Object} This DataProxy object.
13929          * @param {Object} o The data object.
13930          * @param {Object} arg The callback argument object passed to the load function.
13931          */
13932         load : true,
13933         /**
13934          * @event loadexception
13935          * Fires if an Exception occurs during data retrieval.
13936          * @param {Object} This DataProxy object.
13937          * @param {Object} o The data object.
13938          * @param {Object} arg The callback argument object passed to the load function.
13939          * @param {Object} e The Exception.
13940          */
13941         loadexception : true
13942     });
13943     Roo.data.DataProxy.superclass.constructor.call(this);
13944 };
13945
13946 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13947
13948     /**
13949      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13950      */
13951 /*
13952  * Based on:
13953  * Ext JS Library 1.1.1
13954  * Copyright(c) 2006-2007, Ext JS, LLC.
13955  *
13956  * Originally Released Under LGPL - original licence link has changed is not relivant.
13957  *
13958  * Fork - LGPL
13959  * <script type="text/javascript">
13960  */
13961 /**
13962  * @class Roo.data.MemoryProxy
13963  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13964  * to the Reader when its load method is called.
13965  * @constructor
13966  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13967  */
13968 Roo.data.MemoryProxy = function(data){
13969     if (data.data) {
13970         data = data.data;
13971     }
13972     Roo.data.MemoryProxy.superclass.constructor.call(this);
13973     this.data = data;
13974 };
13975
13976 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13977     
13978     /**
13979      * Load data from the requested source (in this case an in-memory
13980      * data object passed to the constructor), read the data object into
13981      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13982      * process that block using the passed callback.
13983      * @param {Object} params This parameter is not used by the MemoryProxy class.
13984      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13985      * object into a block of Roo.data.Records.
13986      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13987      * The function must be passed <ul>
13988      * <li>The Record block object</li>
13989      * <li>The "arg" argument from the load function</li>
13990      * <li>A boolean success indicator</li>
13991      * </ul>
13992      * @param {Object} scope The scope in which to call the callback
13993      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
13994      */
13995     load : function(params, reader, callback, scope, arg){
13996         params = params || {};
13997         var result;
13998         try {
13999             result = reader.readRecords(params.data ? params.data :this.data);
14000         }catch(e){
14001             this.fireEvent("loadexception", this, arg, null, e);
14002             callback.call(scope, null, arg, false);
14003             return;
14004         }
14005         callback.call(scope, result, arg, true);
14006     },
14007     
14008     // private
14009     update : function(params, records){
14010         
14011     }
14012 });/*
14013  * Based on:
14014  * Ext JS Library 1.1.1
14015  * Copyright(c) 2006-2007, Ext JS, LLC.
14016  *
14017  * Originally Released Under LGPL - original licence link has changed is not relivant.
14018  *
14019  * Fork - LGPL
14020  * <script type="text/javascript">
14021  */
14022 /**
14023  * @class Roo.data.HttpProxy
14024  * @extends Roo.data.DataProxy
14025  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14026  * configured to reference a certain URL.<br><br>
14027  * <p>
14028  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14029  * from which the running page was served.<br><br>
14030  * <p>
14031  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14032  * <p>
14033  * Be aware that to enable the browser to parse an XML document, the server must set
14034  * the Content-Type header in the HTTP response to "text/xml".
14035  * @constructor
14036  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14037  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14038  * will be used to make the request.
14039  */
14040 Roo.data.HttpProxy = function(conn){
14041     Roo.data.HttpProxy.superclass.constructor.call(this);
14042     // is conn a conn config or a real conn?
14043     this.conn = conn;
14044     this.useAjax = !conn || !conn.events;
14045   
14046 };
14047
14048 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14049     // thse are take from connection...
14050     
14051     /**
14052      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14053      */
14054     /**
14055      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14056      * extra parameters to each request made by this object. (defaults to undefined)
14057      */
14058     /**
14059      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14060      *  to each request made by this object. (defaults to undefined)
14061      */
14062     /**
14063      * @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)
14064      */
14065     /**
14066      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14067      */
14068      /**
14069      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14070      * @type Boolean
14071      */
14072   
14073
14074     /**
14075      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14076      * @type Boolean
14077      */
14078     /**
14079      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14080      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14081      * a finer-grained basis than the DataProxy events.
14082      */
14083     getConnection : function(){
14084         return this.useAjax ? Roo.Ajax : this.conn;
14085     },
14086
14087     /**
14088      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14089      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14090      * process that block using the passed callback.
14091      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14092      * for the request to the remote server.
14093      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14094      * object into a block of Roo.data.Records.
14095      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14096      * The function must be passed <ul>
14097      * <li>The Record block object</li>
14098      * <li>The "arg" argument from the load function</li>
14099      * <li>A boolean success indicator</li>
14100      * </ul>
14101      * @param {Object} scope The scope in which to call the callback
14102      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14103      */
14104     load : function(params, reader, callback, scope, arg){
14105         if(this.fireEvent("beforeload", this, params) !== false){
14106             var  o = {
14107                 params : params || {},
14108                 request: {
14109                     callback : callback,
14110                     scope : scope,
14111                     arg : arg
14112                 },
14113                 reader: reader,
14114                 callback : this.loadResponse,
14115                 scope: this
14116             };
14117             if(this.useAjax){
14118                 Roo.applyIf(o, this.conn);
14119                 if(this.activeRequest){
14120                     Roo.Ajax.abort(this.activeRequest);
14121                 }
14122                 this.activeRequest = Roo.Ajax.request(o);
14123             }else{
14124                 this.conn.request(o);
14125             }
14126         }else{
14127             callback.call(scope||this, null, arg, false);
14128         }
14129     },
14130
14131     // private
14132     loadResponse : function(o, success, response){
14133         delete this.activeRequest;
14134         if(!success){
14135             this.fireEvent("loadexception", this, o, response);
14136             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14137             return;
14138         }
14139         var result;
14140         try {
14141             result = o.reader.read(response);
14142         }catch(e){
14143             this.fireEvent("loadexception", this, o, response, e);
14144             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14145             return;
14146         }
14147         
14148         this.fireEvent("load", this, o, o.request.arg);
14149         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14150     },
14151
14152     // private
14153     update : function(dataSet){
14154
14155     },
14156
14157     // private
14158     updateResponse : function(dataSet){
14159
14160     }
14161 });/*
14162  * Based on:
14163  * Ext JS Library 1.1.1
14164  * Copyright(c) 2006-2007, Ext JS, LLC.
14165  *
14166  * Originally Released Under LGPL - original licence link has changed is not relivant.
14167  *
14168  * Fork - LGPL
14169  * <script type="text/javascript">
14170  */
14171
14172 /**
14173  * @class Roo.data.ScriptTagProxy
14174  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14175  * other than the originating domain of the running page.<br><br>
14176  * <p>
14177  * <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
14178  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14179  * <p>
14180  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14181  * source code that is used as the source inside a &lt;script> tag.<br><br>
14182  * <p>
14183  * In order for the browser to process the returned data, the server must wrap the data object
14184  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14185  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14186  * depending on whether the callback name was passed:
14187  * <p>
14188  * <pre><code>
14189 boolean scriptTag = false;
14190 String cb = request.getParameter("callback");
14191 if (cb != null) {
14192     scriptTag = true;
14193     response.setContentType("text/javascript");
14194 } else {
14195     response.setContentType("application/x-json");
14196 }
14197 Writer out = response.getWriter();
14198 if (scriptTag) {
14199     out.write(cb + "(");
14200 }
14201 out.print(dataBlock.toJsonString());
14202 if (scriptTag) {
14203     out.write(");");
14204 }
14205 </pre></code>
14206  *
14207  * @constructor
14208  * @param {Object} config A configuration object.
14209  */
14210 Roo.data.ScriptTagProxy = function(config){
14211     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14212     Roo.apply(this, config);
14213     this.head = document.getElementsByTagName("head")[0];
14214 };
14215
14216 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14217
14218 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14219     /**
14220      * @cfg {String} url The URL from which to request the data object.
14221      */
14222     /**
14223      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14224      */
14225     timeout : 30000,
14226     /**
14227      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14228      * the server the name of the callback function set up by the load call to process the returned data object.
14229      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14230      * javascript output which calls this named function passing the data object as its only parameter.
14231      */
14232     callbackParam : "callback",
14233     /**
14234      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14235      * name to the request.
14236      */
14237     nocache : true,
14238
14239     /**
14240      * Load data from the configured URL, read the data object into
14241      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14242      * process that block using the passed callback.
14243      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14244      * for the request to the remote server.
14245      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14246      * object into a block of Roo.data.Records.
14247      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14248      * The function must be passed <ul>
14249      * <li>The Record block object</li>
14250      * <li>The "arg" argument from the load function</li>
14251      * <li>A boolean success indicator</li>
14252      * </ul>
14253      * @param {Object} scope The scope in which to call the callback
14254      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14255      */
14256     load : function(params, reader, callback, scope, arg){
14257         if(this.fireEvent("beforeload", this, params) !== false){
14258
14259             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14260
14261             var url = this.url;
14262             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14263             if(this.nocache){
14264                 url += "&_dc=" + (new Date().getTime());
14265             }
14266             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14267             var trans = {
14268                 id : transId,
14269                 cb : "stcCallback"+transId,
14270                 scriptId : "stcScript"+transId,
14271                 params : params,
14272                 arg : arg,
14273                 url : url,
14274                 callback : callback,
14275                 scope : scope,
14276                 reader : reader
14277             };
14278             var conn = this;
14279
14280             window[trans.cb] = function(o){
14281                 conn.handleResponse(o, trans);
14282             };
14283
14284             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14285
14286             if(this.autoAbort !== false){
14287                 this.abort();
14288             }
14289
14290             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14291
14292             var script = document.createElement("script");
14293             script.setAttribute("src", url);
14294             script.setAttribute("type", "text/javascript");
14295             script.setAttribute("id", trans.scriptId);
14296             this.head.appendChild(script);
14297
14298             this.trans = trans;
14299         }else{
14300             callback.call(scope||this, null, arg, false);
14301         }
14302     },
14303
14304     // private
14305     isLoading : function(){
14306         return this.trans ? true : false;
14307     },
14308
14309     /**
14310      * Abort the current server request.
14311      */
14312     abort : function(){
14313         if(this.isLoading()){
14314             this.destroyTrans(this.trans);
14315         }
14316     },
14317
14318     // private
14319     destroyTrans : function(trans, isLoaded){
14320         this.head.removeChild(document.getElementById(trans.scriptId));
14321         clearTimeout(trans.timeoutId);
14322         if(isLoaded){
14323             window[trans.cb] = undefined;
14324             try{
14325                 delete window[trans.cb];
14326             }catch(e){}
14327         }else{
14328             // if hasn't been loaded, wait for load to remove it to prevent script error
14329             window[trans.cb] = function(){
14330                 window[trans.cb] = undefined;
14331                 try{
14332                     delete window[trans.cb];
14333                 }catch(e){}
14334             };
14335         }
14336     },
14337
14338     // private
14339     handleResponse : function(o, trans){
14340         this.trans = false;
14341         this.destroyTrans(trans, true);
14342         var result;
14343         try {
14344             result = trans.reader.readRecords(o);
14345         }catch(e){
14346             this.fireEvent("loadexception", this, o, trans.arg, e);
14347             trans.callback.call(trans.scope||window, null, trans.arg, false);
14348             return;
14349         }
14350         this.fireEvent("load", this, o, trans.arg);
14351         trans.callback.call(trans.scope||window, result, trans.arg, true);
14352     },
14353
14354     // private
14355     handleFailure : function(trans){
14356         this.trans = false;
14357         this.destroyTrans(trans, false);
14358         this.fireEvent("loadexception", this, null, trans.arg);
14359         trans.callback.call(trans.scope||window, null, trans.arg, false);
14360     }
14361 });/*
14362  * Based on:
14363  * Ext JS Library 1.1.1
14364  * Copyright(c) 2006-2007, Ext JS, LLC.
14365  *
14366  * Originally Released Under LGPL - original licence link has changed is not relivant.
14367  *
14368  * Fork - LGPL
14369  * <script type="text/javascript">
14370  */
14371
14372 /**
14373  * @class Roo.data.JsonReader
14374  * @extends Roo.data.DataReader
14375  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14376  * based on mappings in a provided Roo.data.Record constructor.
14377  * 
14378  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14379  * in the reply previously. 
14380  * 
14381  * <p>
14382  * Example code:
14383  * <pre><code>
14384 var RecordDef = Roo.data.Record.create([
14385     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14386     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14387 ]);
14388 var myReader = new Roo.data.JsonReader({
14389     totalProperty: "results",    // The property which contains the total dataset size (optional)
14390     root: "rows",                // The property which contains an Array of row objects
14391     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14392 }, RecordDef);
14393 </code></pre>
14394  * <p>
14395  * This would consume a JSON file like this:
14396  * <pre><code>
14397 { 'results': 2, 'rows': [
14398     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14399     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14400 }
14401 </code></pre>
14402  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14403  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14404  * paged from the remote server.
14405  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14406  * @cfg {String} root name of the property which contains the Array of row objects.
14407  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14408  * @cfg {Array} fields Array of field definition objects
14409  * @constructor
14410  * Create a new JsonReader
14411  * @param {Object} meta Metadata configuration options
14412  * @param {Object} recordType Either an Array of field definition objects,
14413  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14414  */
14415 Roo.data.JsonReader = function(meta, recordType){
14416     
14417     meta = meta || {};
14418     // set some defaults:
14419     Roo.applyIf(meta, {
14420         totalProperty: 'total',
14421         successProperty : 'success',
14422         root : 'data',
14423         id : 'id'
14424     });
14425     
14426     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14427 };
14428 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14429     
14430     readerType : 'Json',
14431     
14432     /**
14433      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14434      * Used by Store query builder to append _requestMeta to params.
14435      * 
14436      */
14437     metaFromRemote : false,
14438     /**
14439      * This method is only used by a DataProxy which has retrieved data from a remote server.
14440      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14441      * @return {Object} data A data block which is used by an Roo.data.Store object as
14442      * a cache of Roo.data.Records.
14443      */
14444     read : function(response){
14445         var json = response.responseText;
14446        
14447         var o = /* eval:var:o */ eval("("+json+")");
14448         if(!o) {
14449             throw {message: "JsonReader.read: Json object not found"};
14450         }
14451         
14452         if(o.metaData){
14453             
14454             delete this.ef;
14455             this.metaFromRemote = true;
14456             this.meta = o.metaData;
14457             this.recordType = Roo.data.Record.create(o.metaData.fields);
14458             this.onMetaChange(this.meta, this.recordType, o);
14459         }
14460         return this.readRecords(o);
14461     },
14462
14463     // private function a store will implement
14464     onMetaChange : function(meta, recordType, o){
14465
14466     },
14467
14468     /**
14469          * @ignore
14470          */
14471     simpleAccess: function(obj, subsc) {
14472         return obj[subsc];
14473     },
14474
14475         /**
14476          * @ignore
14477          */
14478     getJsonAccessor: function(){
14479         var re = /[\[\.]/;
14480         return function(expr) {
14481             try {
14482                 return(re.test(expr))
14483                     ? new Function("obj", "return obj." + expr)
14484                     : function(obj){
14485                         return obj[expr];
14486                     };
14487             } catch(e){}
14488             return Roo.emptyFn;
14489         };
14490     }(),
14491
14492     /**
14493      * Create a data block containing Roo.data.Records from an XML document.
14494      * @param {Object} o An object which contains an Array of row objects in the property specified
14495      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14496      * which contains the total size of the dataset.
14497      * @return {Object} data A data block which is used by an Roo.data.Store object as
14498      * a cache of Roo.data.Records.
14499      */
14500     readRecords : function(o){
14501         /**
14502          * After any data loads, the raw JSON data is available for further custom processing.
14503          * @type Object
14504          */
14505         this.o = o;
14506         var s = this.meta, Record = this.recordType,
14507             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14508
14509 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14510         if (!this.ef) {
14511             if(s.totalProperty) {
14512                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14513                 }
14514                 if(s.successProperty) {
14515                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14516                 }
14517                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14518                 if (s.id) {
14519                         var g = this.getJsonAccessor(s.id);
14520                         this.getId = function(rec) {
14521                                 var r = g(rec);  
14522                                 return (r === undefined || r === "") ? null : r;
14523                         };
14524                 } else {
14525                         this.getId = function(){return null;};
14526                 }
14527             this.ef = [];
14528             for(var jj = 0; jj < fl; jj++){
14529                 f = fi[jj];
14530                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14531                 this.ef[jj] = this.getJsonAccessor(map);
14532             }
14533         }
14534
14535         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14536         if(s.totalProperty){
14537             var vt = parseInt(this.getTotal(o), 10);
14538             if(!isNaN(vt)){
14539                 totalRecords = vt;
14540             }
14541         }
14542         if(s.successProperty){
14543             var vs = this.getSuccess(o);
14544             if(vs === false || vs === 'false'){
14545                 success = false;
14546             }
14547         }
14548         var records = [];
14549         for(var i = 0; i < c; i++){
14550                 var n = root[i];
14551             var values = {};
14552             var id = this.getId(n);
14553             for(var j = 0; j < fl; j++){
14554                 f = fi[j];
14555             var v = this.ef[j](n);
14556             if (!f.convert) {
14557                 Roo.log('missing convert for ' + f.name);
14558                 Roo.log(f);
14559                 continue;
14560             }
14561             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14562             }
14563             var record = new Record(values, id);
14564             record.json = n;
14565             records[i] = record;
14566         }
14567         return {
14568             raw : o,
14569             success : success,
14570             records : records,
14571             totalRecords : totalRecords
14572         };
14573     },
14574     // used when loading children.. @see loadDataFromChildren
14575     toLoadData: function(rec)
14576     {
14577         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14578         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14579         return { data : data, total : data.length };
14580         
14581     }
14582 });/*
14583  * Based on:
14584  * Ext JS Library 1.1.1
14585  * Copyright(c) 2006-2007, Ext JS, LLC.
14586  *
14587  * Originally Released Under LGPL - original licence link has changed is not relivant.
14588  *
14589  * Fork - LGPL
14590  * <script type="text/javascript">
14591  */
14592
14593 /**
14594  * @class Roo.data.ArrayReader
14595  * @extends Roo.data.DataReader
14596  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14597  * Each element of that Array represents a row of data fields. The
14598  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14599  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14600  * <p>
14601  * Example code:.
14602  * <pre><code>
14603 var RecordDef = Roo.data.Record.create([
14604     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14605     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14606 ]);
14607 var myReader = new Roo.data.ArrayReader({
14608     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14609 }, RecordDef);
14610 </code></pre>
14611  * <p>
14612  * This would consume an Array like this:
14613  * <pre><code>
14614 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14615   </code></pre>
14616  
14617  * @constructor
14618  * Create a new JsonReader
14619  * @param {Object} meta Metadata configuration options.
14620  * @param {Object|Array} recordType Either an Array of field definition objects
14621  * 
14622  * @cfg {Array} fields Array of field definition objects
14623  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14624  * as specified to {@link Roo.data.Record#create},
14625  * or an {@link Roo.data.Record} object
14626  *
14627  * 
14628  * created using {@link Roo.data.Record#create}.
14629  */
14630 Roo.data.ArrayReader = function(meta, recordType)
14631 {    
14632     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634
14635 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14636     
14637       /**
14638      * Create a data block containing Roo.data.Records from an XML document.
14639      * @param {Object} o An Array of row objects which represents the dataset.
14640      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14641      * a cache of Roo.data.Records.
14642      */
14643     readRecords : function(o)
14644     {
14645         var sid = this.meta ? this.meta.id : null;
14646         var recordType = this.recordType, fields = recordType.prototype.fields;
14647         var records = [];
14648         var root = o;
14649         for(var i = 0; i < root.length; i++){
14650                 var n = root[i];
14651             var values = {};
14652             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14653             for(var j = 0, jlen = fields.length; j < jlen; j++){
14654                 var f = fields.items[j];
14655                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14656                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14657                 v = f.convert(v);
14658                 values[f.name] = v;
14659             }
14660             var record = new recordType(values, id);
14661             record.json = n;
14662             records[records.length] = record;
14663         }
14664         return {
14665             records : records,
14666             totalRecords : records.length
14667         };
14668     },
14669     // used when loading children.. @see loadDataFromChildren
14670     toLoadData: function(rec)
14671     {
14672         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14673         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14674         
14675     }
14676     
14677     
14678 });/*
14679  * - LGPL
14680  * * 
14681  */
14682
14683 /**
14684  * @class Roo.bootstrap.ComboBox
14685  * @extends Roo.bootstrap.TriggerField
14686  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14687  * @cfg {Boolean} append (true|false) default false
14688  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14689  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14690  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14691  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14692  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14693  * @cfg {Boolean} animate default true
14694  * @cfg {Boolean} emptyResultText only for touch device
14695  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14696  * @cfg {String} emptyTitle default ''
14697  * @constructor
14698  * Create a new ComboBox.
14699  * @param {Object} config Configuration options
14700  */
14701 Roo.bootstrap.ComboBox = function(config){
14702     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14703     this.addEvents({
14704         /**
14705          * @event expand
14706          * Fires when the dropdown list is expanded
14707         * @param {Roo.bootstrap.ComboBox} combo This combo box
14708         */
14709         'expand' : true,
14710         /**
14711          * @event collapse
14712          * Fires when the dropdown list is collapsed
14713         * @param {Roo.bootstrap.ComboBox} combo This combo box
14714         */
14715         'collapse' : true,
14716         /**
14717          * @event beforeselect
14718          * Fires before a list item is selected. Return false to cancel the selection.
14719         * @param {Roo.bootstrap.ComboBox} combo This combo box
14720         * @param {Roo.data.Record} record The data record returned from the underlying store
14721         * @param {Number} index The index of the selected item in the dropdown list
14722         */
14723         'beforeselect' : true,
14724         /**
14725          * @event select
14726          * Fires when a list item is selected
14727         * @param {Roo.bootstrap.ComboBox} combo This combo box
14728         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14729         * @param {Number} index The index of the selected item in the dropdown list
14730         */
14731         'select' : true,
14732         /**
14733          * @event beforequery
14734          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14735          * The event object passed has these properties:
14736         * @param {Roo.bootstrap.ComboBox} combo This combo box
14737         * @param {String} query The query
14738         * @param {Boolean} forceAll true to force "all" query
14739         * @param {Boolean} cancel true to cancel the query
14740         * @param {Object} e The query event object
14741         */
14742         'beforequery': true,
14743          /**
14744          * @event add
14745          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14746         * @param {Roo.bootstrap.ComboBox} combo This combo box
14747         */
14748         'add' : true,
14749         /**
14750          * @event edit
14751          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14752         * @param {Roo.bootstrap.ComboBox} combo This combo box
14753         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14754         */
14755         'edit' : true,
14756         /**
14757          * @event remove
14758          * Fires when the remove value from the combobox array
14759         * @param {Roo.bootstrap.ComboBox} combo This combo box
14760         */
14761         'remove' : true,
14762         /**
14763          * @event afterremove
14764          * Fires when the remove value from the combobox array
14765         * @param {Roo.bootstrap.ComboBox} combo This combo box
14766         */
14767         'afterremove' : true,
14768         /**
14769          * @event specialfilter
14770          * Fires when specialfilter
14771             * @param {Roo.bootstrap.ComboBox} combo This combo box
14772             */
14773         'specialfilter' : true,
14774         /**
14775          * @event tick
14776          * Fires when tick the element
14777             * @param {Roo.bootstrap.ComboBox} combo This combo box
14778             */
14779         'tick' : true,
14780         /**
14781          * @event touchviewdisplay
14782          * Fires when touch view require special display (default is using displayField)
14783             * @param {Roo.bootstrap.ComboBox} combo This combo box
14784             * @param {Object} cfg set html .
14785             */
14786         'touchviewdisplay' : true
14787         
14788     });
14789     
14790     this.item = [];
14791     this.tickItems = [];
14792     
14793     this.selectedIndex = -1;
14794     if(this.mode == 'local'){
14795         if(config.queryDelay === undefined){
14796             this.queryDelay = 10;
14797         }
14798         if(config.minChars === undefined){
14799             this.minChars = 0;
14800         }
14801     }
14802 };
14803
14804 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14805      
14806     /**
14807      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14808      * rendering into an Roo.Editor, defaults to false)
14809      */
14810     /**
14811      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14812      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14813      */
14814     /**
14815      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14816      */
14817     /**
14818      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14819      * the dropdown list (defaults to undefined, with no header element)
14820      */
14821
14822      /**
14823      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14824      */
14825      
14826      /**
14827      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14828      */
14829     listWidth: undefined,
14830     /**
14831      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14832      * mode = 'remote' or 'text' if mode = 'local')
14833      */
14834     displayField: undefined,
14835     
14836     /**
14837      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14838      * mode = 'remote' or 'value' if mode = 'local'). 
14839      * Note: use of a valueField requires the user make a selection
14840      * in order for a value to be mapped.
14841      */
14842     valueField: undefined,
14843     /**
14844      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14845      */
14846     modalTitle : '',
14847     
14848     /**
14849      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14850      * field's data value (defaults to the underlying DOM element's name)
14851      */
14852     hiddenName: undefined,
14853     /**
14854      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14855      */
14856     listClass: '',
14857     /**
14858      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14859      */
14860     selectedClass: 'active',
14861     
14862     /**
14863      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14864      */
14865     shadow:'sides',
14866     /**
14867      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14868      * anchor positions (defaults to 'tl-bl')
14869      */
14870     listAlign: 'tl-bl?',
14871     /**
14872      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14873      */
14874     maxHeight: 300,
14875     /**
14876      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14877      * query specified by the allQuery config option (defaults to 'query')
14878      */
14879     triggerAction: 'query',
14880     /**
14881      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14882      * (defaults to 4, does not apply if editable = false)
14883      */
14884     minChars : 4,
14885     /**
14886      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14887      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14888      */
14889     typeAhead: false,
14890     /**
14891      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14892      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14893      */
14894     queryDelay: 500,
14895     /**
14896      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14897      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14898      */
14899     pageSize: 0,
14900     /**
14901      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14902      * when editable = true (defaults to false)
14903      */
14904     selectOnFocus:false,
14905     /**
14906      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14907      */
14908     queryParam: 'query',
14909     /**
14910      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14911      * when mode = 'remote' (defaults to 'Loading...')
14912      */
14913     loadingText: 'Loading...',
14914     /**
14915      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14916      */
14917     resizable: false,
14918     /**
14919      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14920      */
14921     handleHeight : 8,
14922     /**
14923      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14924      * traditional select (defaults to true)
14925      */
14926     editable: true,
14927     /**
14928      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14929      */
14930     allQuery: '',
14931     /**
14932      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14933      */
14934     mode: 'remote',
14935     /**
14936      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14937      * listWidth has a higher value)
14938      */
14939     minListWidth : 70,
14940     /**
14941      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14942      * allow the user to set arbitrary text into the field (defaults to false)
14943      */
14944     forceSelection:false,
14945     /**
14946      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14947      * if typeAhead = true (defaults to 250)
14948      */
14949     typeAheadDelay : 250,
14950     /**
14951      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14952      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14953      */
14954     valueNotFoundText : undefined,
14955     /**
14956      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14957      */
14958     blockFocus : false,
14959     
14960     /**
14961      * @cfg {Boolean} disableClear Disable showing of clear button.
14962      */
14963     disableClear : false,
14964     /**
14965      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14966      */
14967     alwaysQuery : false,
14968     
14969     /**
14970      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14971      */
14972     multiple : false,
14973     
14974     /**
14975      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14976      */
14977     invalidClass : "has-warning",
14978     
14979     /**
14980      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14981      */
14982     validClass : "has-success",
14983     
14984     /**
14985      * @cfg {Boolean} specialFilter (true|false) special filter default false
14986      */
14987     specialFilter : false,
14988     
14989     /**
14990      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
14991      */
14992     mobileTouchView : true,
14993     
14994     /**
14995      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
14996      */
14997     useNativeIOS : false,
14998     
14999     /**
15000      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15001      */
15002     mobile_restrict_height : false,
15003     
15004     ios_options : false,
15005     
15006     //private
15007     addicon : false,
15008     editicon: false,
15009     
15010     page: 0,
15011     hasQuery: false,
15012     append: false,
15013     loadNext: false,
15014     autoFocus : true,
15015     tickable : false,
15016     btnPosition : 'right',
15017     triggerList : true,
15018     showToggleBtn : true,
15019     animate : true,
15020     emptyResultText: 'Empty',
15021     triggerText : 'Select',
15022     emptyTitle : '',
15023     
15024     // element that contains real text value.. (when hidden is used..)
15025     
15026     getAutoCreate : function()
15027     {   
15028         var cfg = false;
15029         //render
15030         /*
15031          * Render classic select for iso
15032          */
15033         
15034         if(Roo.isIOS && this.useNativeIOS){
15035             cfg = this.getAutoCreateNativeIOS();
15036             return cfg;
15037         }
15038         
15039         /*
15040          * Touch Devices
15041          */
15042         
15043         if(Roo.isTouch && this.mobileTouchView){
15044             cfg = this.getAutoCreateTouchView();
15045             return cfg;;
15046         }
15047         
15048         /*
15049          *  Normal ComboBox
15050          */
15051         if(!this.tickable){
15052             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15053             return cfg;
15054         }
15055         
15056         /*
15057          *  ComboBox with tickable selections
15058          */
15059              
15060         var align = this.labelAlign || this.parentLabelAlign();
15061         
15062         cfg = {
15063             cls : 'form-group roo-combobox-tickable' //input-group
15064         };
15065         
15066         var btn_text_select = '';
15067         var btn_text_done = '';
15068         var btn_text_cancel = '';
15069         
15070         if (this.btn_text_show) {
15071             btn_text_select = 'Select';
15072             btn_text_done = 'Done';
15073             btn_text_cancel = 'Cancel'; 
15074         }
15075         
15076         var buttons = {
15077             tag : 'div',
15078             cls : 'tickable-buttons',
15079             cn : [
15080                 {
15081                     tag : 'button',
15082                     type : 'button',
15083                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15084                     //html : this.triggerText
15085                     html: btn_text_select
15086                 },
15087                 {
15088                     tag : 'button',
15089                     type : 'button',
15090                     name : 'ok',
15091                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15092                     //html : 'Done'
15093                     html: btn_text_done
15094                 },
15095                 {
15096                     tag : 'button',
15097                     type : 'button',
15098                     name : 'cancel',
15099                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15100                     //html : 'Cancel'
15101                     html: btn_text_cancel
15102                 }
15103             ]
15104         };
15105         
15106         if(this.editable){
15107             buttons.cn.unshift({
15108                 tag: 'input',
15109                 cls: 'roo-select2-search-field-input'
15110             });
15111         }
15112         
15113         var _this = this;
15114         
15115         Roo.each(buttons.cn, function(c){
15116             if (_this.size) {
15117                 c.cls += ' btn-' + _this.size;
15118             }
15119
15120             if (_this.disabled) {
15121                 c.disabled = true;
15122             }
15123         });
15124         
15125         var box = {
15126             tag: 'div',
15127             style : 'display: contents',
15128             cn: [
15129                 {
15130                     tag: 'input',
15131                     type : 'hidden',
15132                     cls: 'form-hidden-field'
15133                 },
15134                 {
15135                     tag: 'ul',
15136                     cls: 'roo-select2-choices',
15137                     cn:[
15138                         {
15139                             tag: 'li',
15140                             cls: 'roo-select2-search-field',
15141                             cn: [
15142                                 buttons
15143                             ]
15144                         }
15145                     ]
15146                 }
15147             ]
15148         };
15149         
15150         var combobox = {
15151             cls: 'roo-select2-container input-group roo-select2-container-multi',
15152             cn: [
15153                 
15154                 box
15155 //                {
15156 //                    tag: 'ul',
15157 //                    cls: 'typeahead typeahead-long dropdown-menu',
15158 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15159 //                }
15160             ]
15161         };
15162         
15163         if(this.hasFeedback && !this.allowBlank){
15164             
15165             var feedback = {
15166                 tag: 'span',
15167                 cls: 'glyphicon form-control-feedback'
15168             };
15169
15170             combobox.cn.push(feedback);
15171         }
15172         
15173         
15174         
15175         var indicator = {
15176             tag : 'i',
15177             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15178             tooltip : 'This field is required'
15179         };
15180         if (Roo.bootstrap.version == 4) {
15181             indicator = {
15182                 tag : 'i',
15183                 style : 'display:none'
15184             };
15185         }
15186         if (align ==='left' && this.fieldLabel.length) {
15187             
15188             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15189             
15190             cfg.cn = [
15191                 indicator,
15192                 {
15193                     tag: 'label',
15194                     'for' :  id,
15195                     cls : 'control-label col-form-label',
15196                     html : this.fieldLabel
15197
15198                 },
15199                 {
15200                     cls : "", 
15201                     cn: [
15202                         combobox
15203                     ]
15204                 }
15205
15206             ];
15207             
15208             var labelCfg = cfg.cn[1];
15209             var contentCfg = cfg.cn[2];
15210             
15211
15212             if(this.indicatorpos == 'right'){
15213                 
15214                 cfg.cn = [
15215                     {
15216                         tag: 'label',
15217                         'for' :  id,
15218                         cls : 'control-label col-form-label',
15219                         cn : [
15220                             {
15221                                 tag : 'span',
15222                                 html : this.fieldLabel
15223                             },
15224                             indicator
15225                         ]
15226                     },
15227                     {
15228                         cls : "",
15229                         cn: [
15230                             combobox
15231                         ]
15232                     }
15233
15234                 ];
15235                 
15236                 
15237                 
15238                 labelCfg = cfg.cn[0];
15239                 contentCfg = cfg.cn[1];
15240             
15241             }
15242             
15243             if(this.labelWidth > 12){
15244                 labelCfg.style = "width: " + this.labelWidth + 'px';
15245             }
15246             
15247             if(this.labelWidth < 13 && this.labelmd == 0){
15248                 this.labelmd = this.labelWidth;
15249             }
15250             
15251             if(this.labellg > 0){
15252                 labelCfg.cls += ' col-lg-' + this.labellg;
15253                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15254             }
15255             
15256             if(this.labelmd > 0){
15257                 labelCfg.cls += ' col-md-' + this.labelmd;
15258                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15259             }
15260             
15261             if(this.labelsm > 0){
15262                 labelCfg.cls += ' col-sm-' + this.labelsm;
15263                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15264             }
15265             
15266             if(this.labelxs > 0){
15267                 labelCfg.cls += ' col-xs-' + this.labelxs;
15268                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15269             }
15270                 
15271                 
15272         } else if ( this.fieldLabel.length) {
15273 //                Roo.log(" label");
15274                  cfg.cn = [
15275                    indicator,
15276                     {
15277                         tag: 'label',
15278                         //cls : 'input-group-addon',
15279                         html : this.fieldLabel
15280                     },
15281                     combobox
15282                 ];
15283                 
15284                 if(this.indicatorpos == 'right'){
15285                     cfg.cn = [
15286                         {
15287                             tag: 'label',
15288                             //cls : 'input-group-addon',
15289                             html : this.fieldLabel
15290                         },
15291                         indicator,
15292                         combobox
15293                     ];
15294                     
15295                 }
15296
15297         } else {
15298             
15299 //                Roo.log(" no label && no align");
15300                 cfg = combobox
15301                      
15302                 
15303         }
15304          
15305         var settings=this;
15306         ['xs','sm','md','lg'].map(function(size){
15307             if (settings[size]) {
15308                 cfg.cls += ' col-' + size + '-' + settings[size];
15309             }
15310         });
15311         
15312         return cfg;
15313         
15314     },
15315     
15316     _initEventsCalled : false,
15317     
15318     // private
15319     initEvents: function()
15320     {   
15321         if (this._initEventsCalled) { // as we call render... prevent looping...
15322             return;
15323         }
15324         this._initEventsCalled = true;
15325         
15326         if (!this.store) {
15327             throw "can not find store for combo";
15328         }
15329         
15330         this.indicator = this.indicatorEl();
15331         
15332         this.store = Roo.factory(this.store, Roo.data);
15333         this.store.parent = this;
15334         
15335         // if we are building from html. then this element is so complex, that we can not really
15336         // use the rendered HTML.
15337         // so we have to trash and replace the previous code.
15338         if (Roo.XComponent.build_from_html) {
15339             // remove this element....
15340             var e = this.el.dom, k=0;
15341             while (e ) { e = e.previousSibling;  ++k;}
15342
15343             this.el.remove();
15344             
15345             this.el=false;
15346             this.rendered = false;
15347             
15348             this.render(this.parent().getChildContainer(true), k);
15349         }
15350         
15351         if(Roo.isIOS && this.useNativeIOS){
15352             this.initIOSView();
15353             return;
15354         }
15355         
15356         /*
15357          * Touch Devices
15358          */
15359         
15360         if(Roo.isTouch && this.mobileTouchView){
15361             this.initTouchView();
15362             return;
15363         }
15364         
15365         if(this.tickable){
15366             this.initTickableEvents();
15367             return;
15368         }
15369         
15370         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15371         
15372         if(this.hiddenName){
15373             
15374             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15375             
15376             this.hiddenField.dom.value =
15377                 this.hiddenValue !== undefined ? this.hiddenValue :
15378                 this.value !== undefined ? this.value : '';
15379
15380             // prevent input submission
15381             this.el.dom.removeAttribute('name');
15382             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15383              
15384              
15385         }
15386         //if(Roo.isGecko){
15387         //    this.el.dom.setAttribute('autocomplete', 'off');
15388         //}
15389         
15390         var cls = 'x-combo-list';
15391         
15392         //this.list = new Roo.Layer({
15393         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15394         //});
15395         
15396         var _this = this;
15397         
15398         (function(){
15399             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15400             _this.list.setWidth(lw);
15401         }).defer(100);
15402         
15403         this.list.on('mouseover', this.onViewOver, this);
15404         this.list.on('mousemove', this.onViewMove, this);
15405         this.list.on('scroll', this.onViewScroll, this);
15406         
15407         /*
15408         this.list.swallowEvent('mousewheel');
15409         this.assetHeight = 0;
15410
15411         if(this.title){
15412             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15413             this.assetHeight += this.header.getHeight();
15414         }
15415
15416         this.innerList = this.list.createChild({cls:cls+'-inner'});
15417         this.innerList.on('mouseover', this.onViewOver, this);
15418         this.innerList.on('mousemove', this.onViewMove, this);
15419         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15420         
15421         if(this.allowBlank && !this.pageSize && !this.disableClear){
15422             this.footer = this.list.createChild({cls:cls+'-ft'});
15423             this.pageTb = new Roo.Toolbar(this.footer);
15424            
15425         }
15426         if(this.pageSize){
15427             this.footer = this.list.createChild({cls:cls+'-ft'});
15428             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15429                     {pageSize: this.pageSize});
15430             
15431         }
15432         
15433         if (this.pageTb && this.allowBlank && !this.disableClear) {
15434             var _this = this;
15435             this.pageTb.add(new Roo.Toolbar.Fill(), {
15436                 cls: 'x-btn-icon x-btn-clear',
15437                 text: '&#160;',
15438                 handler: function()
15439                 {
15440                     _this.collapse();
15441                     _this.clearValue();
15442                     _this.onSelect(false, -1);
15443                 }
15444             });
15445         }
15446         if (this.footer) {
15447             this.assetHeight += this.footer.getHeight();
15448         }
15449         */
15450             
15451         if(!this.tpl){
15452             this.tpl = Roo.bootstrap.version == 4 ?
15453                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15454                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15455         }
15456
15457         this.view = new Roo.View(this.list, this.tpl, {
15458             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15459         });
15460         //this.view.wrapEl.setDisplayed(false);
15461         this.view.on('click', this.onViewClick, this);
15462         
15463         
15464         this.store.on('beforeload', this.onBeforeLoad, this);
15465         this.store.on('load', this.onLoad, this);
15466         this.store.on('loadexception', this.onLoadException, this);
15467         /*
15468         if(this.resizable){
15469             this.resizer = new Roo.Resizable(this.list,  {
15470                pinned:true, handles:'se'
15471             });
15472             this.resizer.on('resize', function(r, w, h){
15473                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15474                 this.listWidth = w;
15475                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15476                 this.restrictHeight();
15477             }, this);
15478             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15479         }
15480         */
15481         if(!this.editable){
15482             this.editable = true;
15483             this.setEditable(false);
15484         }
15485         
15486         /*
15487         
15488         if (typeof(this.events.add.listeners) != 'undefined') {
15489             
15490             this.addicon = this.wrap.createChild(
15491                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15492        
15493             this.addicon.on('click', function(e) {
15494                 this.fireEvent('add', this);
15495             }, this);
15496         }
15497         if (typeof(this.events.edit.listeners) != 'undefined') {
15498             
15499             this.editicon = this.wrap.createChild(
15500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15501             if (this.addicon) {
15502                 this.editicon.setStyle('margin-left', '40px');
15503             }
15504             this.editicon.on('click', function(e) {
15505                 
15506                 // we fire even  if inothing is selected..
15507                 this.fireEvent('edit', this, this.lastData );
15508                 
15509             }, this);
15510         }
15511         */
15512         
15513         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15514             "up" : function(e){
15515                 this.inKeyMode = true;
15516                 this.selectPrev();
15517             },
15518
15519             "down" : function(e){
15520                 if(!this.isExpanded()){
15521                     this.onTriggerClick();
15522                 }else{
15523                     this.inKeyMode = true;
15524                     this.selectNext();
15525                 }
15526             },
15527
15528             "enter" : function(e){
15529 //                this.onViewClick();
15530                 //return true;
15531                 this.collapse();
15532                 
15533                 if(this.fireEvent("specialkey", this, e)){
15534                     this.onViewClick(false);
15535                 }
15536                 
15537                 return true;
15538             },
15539
15540             "esc" : function(e){
15541                 this.collapse();
15542             },
15543
15544             "tab" : function(e){
15545                 this.collapse();
15546                 
15547                 if(this.fireEvent("specialkey", this, e)){
15548                     this.onViewClick(false);
15549                 }
15550                 
15551                 return true;
15552             },
15553
15554             scope : this,
15555
15556             doRelay : function(foo, bar, hname){
15557                 if(hname == 'down' || this.scope.isExpanded()){
15558                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15559                 }
15560                 return true;
15561             },
15562
15563             forceKeyDown: true
15564         });
15565         
15566         
15567         this.queryDelay = Math.max(this.queryDelay || 10,
15568                 this.mode == 'local' ? 10 : 250);
15569         
15570         
15571         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15572         
15573         if(this.typeAhead){
15574             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15575         }
15576         if(this.editable !== false){
15577             this.inputEl().on("keyup", this.onKeyUp, this);
15578         }
15579         if(this.forceSelection){
15580             this.inputEl().on('blur', this.doForce, this);
15581         }
15582         
15583         if(this.multiple){
15584             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15585             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15586         }
15587     },
15588     
15589     initTickableEvents: function()
15590     {   
15591         this.createList();
15592         
15593         if(this.hiddenName){
15594             
15595             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15596             
15597             this.hiddenField.dom.value =
15598                 this.hiddenValue !== undefined ? this.hiddenValue :
15599                 this.value !== undefined ? this.value : '';
15600
15601             // prevent input submission
15602             this.el.dom.removeAttribute('name');
15603             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15604              
15605              
15606         }
15607         
15608 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15609         
15610         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15611         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15612         if(this.triggerList){
15613             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15614         }
15615          
15616         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15617         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15618         
15619         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15620         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15621         
15622         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15623         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15624         
15625         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15626         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15627         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15628         
15629         this.okBtn.hide();
15630         this.cancelBtn.hide();
15631         
15632         var _this = this;
15633         
15634         (function(){
15635             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15636             _this.list.setWidth(lw);
15637         }).defer(100);
15638         
15639         this.list.on('mouseover', this.onViewOver, this);
15640         this.list.on('mousemove', this.onViewMove, this);
15641         
15642         this.list.on('scroll', this.onViewScroll, this);
15643         
15644         if(!this.tpl){
15645             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15646                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15647         }
15648
15649         this.view = new Roo.View(this.list, this.tpl, {
15650             singleSelect:true,
15651             tickable:true,
15652             parent:this,
15653             store: this.store,
15654             selectedClass: this.selectedClass
15655         });
15656         
15657         //this.view.wrapEl.setDisplayed(false);
15658         this.view.on('click', this.onViewClick, this);
15659         
15660         
15661         
15662         this.store.on('beforeload', this.onBeforeLoad, this);
15663         this.store.on('load', this.onLoad, this);
15664         this.store.on('loadexception', this.onLoadException, this);
15665         
15666         if(this.editable){
15667             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15668                 "up" : function(e){
15669                     this.inKeyMode = true;
15670                     this.selectPrev();
15671                 },
15672
15673                 "down" : function(e){
15674                     this.inKeyMode = true;
15675                     this.selectNext();
15676                 },
15677
15678                 "enter" : function(e){
15679                     if(this.fireEvent("specialkey", this, e)){
15680                         this.onViewClick(false);
15681                     }
15682                     
15683                     return true;
15684                 },
15685
15686                 "esc" : function(e){
15687                     this.onTickableFooterButtonClick(e, false, false);
15688                 },
15689
15690                 "tab" : function(e){
15691                     this.fireEvent("specialkey", this, e);
15692                     
15693                     this.onTickableFooterButtonClick(e, false, false);
15694                     
15695                     return true;
15696                 },
15697
15698                 scope : this,
15699
15700                 doRelay : function(e, fn, key){
15701                     if(this.scope.isExpanded()){
15702                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15703                     }
15704                     return true;
15705                 },
15706
15707                 forceKeyDown: true
15708             });
15709         }
15710         
15711         this.queryDelay = Math.max(this.queryDelay || 10,
15712                 this.mode == 'local' ? 10 : 250);
15713         
15714         
15715         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15716         
15717         if(this.typeAhead){
15718             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15719         }
15720         
15721         if(this.editable !== false){
15722             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15723         }
15724         
15725         this.indicator = this.indicatorEl();
15726         
15727         if(this.indicator){
15728             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15729             this.indicator.hide();
15730         }
15731         
15732     },
15733
15734     onDestroy : function(){
15735         if(this.view){
15736             this.view.setStore(null);
15737             this.view.el.removeAllListeners();
15738             this.view.el.remove();
15739             this.view.purgeListeners();
15740         }
15741         if(this.list){
15742             this.list.dom.innerHTML  = '';
15743         }
15744         
15745         if(this.store){
15746             this.store.un('beforeload', this.onBeforeLoad, this);
15747             this.store.un('load', this.onLoad, this);
15748             this.store.un('loadexception', this.onLoadException, this);
15749         }
15750         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15751     },
15752
15753     // private
15754     fireKey : function(e){
15755         if(e.isNavKeyPress() && !this.list.isVisible()){
15756             this.fireEvent("specialkey", this, e);
15757         }
15758     },
15759
15760     // private
15761     onResize: function(w, h){
15762 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15763 //        
15764 //        if(typeof w != 'number'){
15765 //            // we do not handle it!?!?
15766 //            return;
15767 //        }
15768 //        var tw = this.trigger.getWidth();
15769 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15770 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15771 //        var x = w - tw;
15772 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15773 //            
15774 //        //this.trigger.setStyle('left', x+'px');
15775 //        
15776 //        if(this.list && this.listWidth === undefined){
15777 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15778 //            this.list.setWidth(lw);
15779 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15780 //        }
15781         
15782     
15783         
15784     },
15785
15786     /**
15787      * Allow or prevent the user from directly editing the field text.  If false is passed,
15788      * the user will only be able to select from the items defined in the dropdown list.  This method
15789      * is the runtime equivalent of setting the 'editable' config option at config time.
15790      * @param {Boolean} value True to allow the user to directly edit the field text
15791      */
15792     setEditable : function(value){
15793         if(value == this.editable){
15794             return;
15795         }
15796         this.editable = value;
15797         if(!value){
15798             this.inputEl().dom.setAttribute('readOnly', true);
15799             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15800             this.inputEl().addClass('x-combo-noedit');
15801         }else{
15802             this.inputEl().dom.setAttribute('readOnly', false);
15803             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15804             this.inputEl().removeClass('x-combo-noedit');
15805         }
15806     },
15807
15808     // private
15809     
15810     onBeforeLoad : function(combo,opts){
15811         if(!this.hasFocus){
15812             return;
15813         }
15814          if (!opts.add) {
15815             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15816          }
15817         this.restrictHeight();
15818         this.selectedIndex = -1;
15819     },
15820
15821     // private
15822     onLoad : function(){
15823         
15824         this.hasQuery = false;
15825         
15826         if(!this.hasFocus){
15827             return;
15828         }
15829         
15830         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15831             this.loading.hide();
15832         }
15833         
15834         if(this.store.getCount() > 0){
15835             
15836             this.expand();
15837             this.restrictHeight();
15838             if(this.lastQuery == this.allQuery){
15839                 if(this.editable && !this.tickable){
15840                     this.inputEl().dom.select();
15841                 }
15842                 
15843                 if(
15844                     !this.selectByValue(this.value, true) &&
15845                     this.autoFocus && 
15846                     (
15847                         !this.store.lastOptions ||
15848                         typeof(this.store.lastOptions.add) == 'undefined' || 
15849                         this.store.lastOptions.add != true
15850                     )
15851                 ){
15852                     this.select(0, true);
15853                 }
15854             }else{
15855                 if(this.autoFocus){
15856                     this.selectNext();
15857                 }
15858                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15859                     this.taTask.delay(this.typeAheadDelay);
15860                 }
15861             }
15862         }else{
15863             this.onEmptyResults();
15864         }
15865         
15866         //this.el.focus();
15867     },
15868     // private
15869     onLoadException : function()
15870     {
15871         this.hasQuery = false;
15872         
15873         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15874             this.loading.hide();
15875         }
15876         
15877         if(this.tickable && this.editable){
15878             return;
15879         }
15880         
15881         this.collapse();
15882         // only causes errors at present
15883         //Roo.log(this.store.reader.jsonData);
15884         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15885             // fixme
15886             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15887         //}
15888         
15889         
15890     },
15891     // private
15892     onTypeAhead : function(){
15893         if(this.store.getCount() > 0){
15894             var r = this.store.getAt(0);
15895             var newValue = r.data[this.displayField];
15896             var len = newValue.length;
15897             var selStart = this.getRawValue().length;
15898             
15899             if(selStart != len){
15900                 this.setRawValue(newValue);
15901                 this.selectText(selStart, newValue.length);
15902             }
15903         }
15904     },
15905
15906     // private
15907     onSelect : function(record, index){
15908         
15909         if(this.fireEvent('beforeselect', this, record, index) !== false){
15910         
15911             this.setFromData(index > -1 ? record.data : false);
15912             
15913             this.collapse();
15914             this.fireEvent('select', this, record, index);
15915         }
15916     },
15917
15918     /**
15919      * Returns the currently selected field value or empty string if no value is set.
15920      * @return {String} value The selected value
15921      */
15922     getValue : function()
15923     {
15924         if(Roo.isIOS && this.useNativeIOS){
15925             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15926         }
15927         
15928         if(this.multiple){
15929             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15930         }
15931         
15932         if(this.valueField){
15933             return typeof this.value != 'undefined' ? this.value : '';
15934         }else{
15935             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15936         }
15937     },
15938     
15939     getRawValue : function()
15940     {
15941         if(Roo.isIOS && this.useNativeIOS){
15942             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15943         }
15944         
15945         var v = this.inputEl().getValue();
15946         
15947         return v;
15948     },
15949
15950     /**
15951      * Clears any text/value currently set in the field
15952      */
15953     clearValue : function(){
15954         
15955         if(this.hiddenField){
15956             this.hiddenField.dom.value = '';
15957         }
15958         this.value = '';
15959         this.setRawValue('');
15960         this.lastSelectionText = '';
15961         this.lastData = false;
15962         
15963         var close = this.closeTriggerEl();
15964         
15965         if(close){
15966             close.hide();
15967         }
15968         
15969         this.validate();
15970         
15971     },
15972
15973     /**
15974      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15975      * will be displayed in the field.  If the value does not match the data value of an existing item,
15976      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15977      * Otherwise the field will be blank (although the value will still be set).
15978      * @param {String} value The value to match
15979      */
15980     setValue : function(v)
15981     {
15982         if(Roo.isIOS && this.useNativeIOS){
15983             this.setIOSValue(v);
15984             return;
15985         }
15986         
15987         if(this.multiple){
15988             this.syncValue();
15989             return;
15990         }
15991         
15992         var text = v;
15993         if(this.valueField){
15994             var r = this.findRecord(this.valueField, v);
15995             if(r){
15996                 text = r.data[this.displayField];
15997             }else if(this.valueNotFoundText !== undefined){
15998                 text = this.valueNotFoundText;
15999             }
16000         }
16001         this.lastSelectionText = text;
16002         if(this.hiddenField){
16003             this.hiddenField.dom.value = v;
16004         }
16005         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16006         this.value = v;
16007         
16008         var close = this.closeTriggerEl();
16009         
16010         if(close){
16011             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16012         }
16013         
16014         this.validate();
16015     },
16016     /**
16017      * @property {Object} the last set data for the element
16018      */
16019     
16020     lastData : false,
16021     /**
16022      * Sets the value of the field based on a object which is related to the record format for the store.
16023      * @param {Object} value the value to set as. or false on reset?
16024      */
16025     setFromData : function(o){
16026         
16027         if(this.multiple){
16028             this.addItem(o);
16029             return;
16030         }
16031             
16032         var dv = ''; // display value
16033         var vv = ''; // value value..
16034         this.lastData = o;
16035         if (this.displayField) {
16036             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16037         } else {
16038             // this is an error condition!!!
16039             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16040         }
16041         
16042         if(this.valueField){
16043             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16044         }
16045         
16046         var close = this.closeTriggerEl();
16047         
16048         if(close){
16049             if(dv.length || vv * 1 > 0){
16050                 close.show() ;
16051                 this.blockFocus=true;
16052             } else {
16053                 close.hide();
16054             }             
16055         }
16056         
16057         if(this.hiddenField){
16058             this.hiddenField.dom.value = vv;
16059             
16060             this.lastSelectionText = dv;
16061             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16062             this.value = vv;
16063             return;
16064         }
16065         // no hidden field.. - we store the value in 'value', but still display
16066         // display field!!!!
16067         this.lastSelectionText = dv;
16068         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16069         this.value = vv;
16070         
16071         
16072         
16073     },
16074     // private
16075     reset : function(){
16076         // overridden so that last data is reset..
16077         
16078         if(this.multiple){
16079             this.clearItem();
16080             return;
16081         }
16082         
16083         this.setValue(this.originalValue);
16084         //this.clearInvalid();
16085         this.lastData = false;
16086         if (this.view) {
16087             this.view.clearSelections();
16088         }
16089         
16090         this.validate();
16091     },
16092     // private
16093     findRecord : function(prop, value){
16094         var record;
16095         if(this.store.getCount() > 0){
16096             this.store.each(function(r){
16097                 if(r.data[prop] == value){
16098                     record = r;
16099                     return false;
16100                 }
16101                 return true;
16102             });
16103         }
16104         return record;
16105     },
16106     
16107     getName: function()
16108     {
16109         // returns hidden if it's set..
16110         if (!this.rendered) {return ''};
16111         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16112         
16113     },
16114     // private
16115     onViewMove : function(e, t){
16116         this.inKeyMode = false;
16117     },
16118
16119     // private
16120     onViewOver : function(e, t){
16121         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16122             return;
16123         }
16124         var item = this.view.findItemFromChild(t);
16125         
16126         if(item){
16127             var index = this.view.indexOf(item);
16128             this.select(index, false);
16129         }
16130     },
16131
16132     // private
16133     onViewClick : function(view, doFocus, el, e)
16134     {
16135         var index = this.view.getSelectedIndexes()[0];
16136         
16137         var r = this.store.getAt(index);
16138         
16139         if(this.tickable){
16140             
16141             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16142                 return;
16143             }
16144             
16145             var rm = false;
16146             var _this = this;
16147             
16148             Roo.each(this.tickItems, function(v,k){
16149                 
16150                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16151                     Roo.log(v);
16152                     _this.tickItems.splice(k, 1);
16153                     
16154                     if(typeof(e) == 'undefined' && view == false){
16155                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16156                     }
16157                     
16158                     rm = true;
16159                     return;
16160                 }
16161             });
16162             
16163             if(rm){
16164                 return;
16165             }
16166             
16167             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16168                 this.tickItems.push(r.data);
16169             }
16170             
16171             if(typeof(e) == 'undefined' && view == false){
16172                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16173             }
16174                     
16175             return;
16176         }
16177         
16178         if(r){
16179             this.onSelect(r, index);
16180         }
16181         if(doFocus !== false && !this.blockFocus){
16182             this.inputEl().focus();
16183         }
16184     },
16185
16186     // private
16187     restrictHeight : function(){
16188         //this.innerList.dom.style.height = '';
16189         //var inner = this.innerList.dom;
16190         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16191         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16192         //this.list.beginUpdate();
16193         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16194         this.list.alignTo(this.inputEl(), this.listAlign);
16195         this.list.alignTo(this.inputEl(), this.listAlign);
16196         //this.list.endUpdate();
16197     },
16198
16199     // private
16200     onEmptyResults : function(){
16201         
16202         if(this.tickable && this.editable){
16203             this.hasFocus = false;
16204             this.restrictHeight();
16205             return;
16206         }
16207         
16208         this.collapse();
16209     },
16210
16211     /**
16212      * Returns true if the dropdown list is expanded, else false.
16213      */
16214     isExpanded : function(){
16215         return this.list.isVisible();
16216     },
16217
16218     /**
16219      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16220      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16221      * @param {String} value The data value of the item to select
16222      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16223      * selected item if it is not currently in view (defaults to true)
16224      * @return {Boolean} True if the value matched an item in the list, else false
16225      */
16226     selectByValue : function(v, scrollIntoView){
16227         if(v !== undefined && v !== null){
16228             var r = this.findRecord(this.valueField || this.displayField, v);
16229             if(r){
16230                 this.select(this.store.indexOf(r), scrollIntoView);
16231                 return true;
16232             }
16233         }
16234         return false;
16235     },
16236
16237     /**
16238      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16239      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16240      * @param {Number} index The zero-based index of the list item to select
16241      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16242      * selected item if it is not currently in view (defaults to true)
16243      */
16244     select : function(index, scrollIntoView){
16245         this.selectedIndex = index;
16246         this.view.select(index);
16247         if(scrollIntoView !== false){
16248             var el = this.view.getNode(index);
16249             /*
16250              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16251              */
16252             if(el){
16253                 this.list.scrollChildIntoView(el, false);
16254             }
16255         }
16256     },
16257
16258     // private
16259     selectNext : function(){
16260         var ct = this.store.getCount();
16261         if(ct > 0){
16262             if(this.selectedIndex == -1){
16263                 this.select(0);
16264             }else if(this.selectedIndex < ct-1){
16265                 this.select(this.selectedIndex+1);
16266             }
16267         }
16268     },
16269
16270     // private
16271     selectPrev : function(){
16272         var ct = this.store.getCount();
16273         if(ct > 0){
16274             if(this.selectedIndex == -1){
16275                 this.select(0);
16276             }else if(this.selectedIndex != 0){
16277                 this.select(this.selectedIndex-1);
16278             }
16279         }
16280     },
16281
16282     // private
16283     onKeyUp : function(e){
16284         if(this.editable !== false && !e.isSpecialKey()){
16285             this.lastKey = e.getKey();
16286             this.dqTask.delay(this.queryDelay);
16287         }
16288     },
16289
16290     // private
16291     validateBlur : function(){
16292         return !this.list || !this.list.isVisible();   
16293     },
16294
16295     // private
16296     initQuery : function(){
16297         
16298         var v = this.getRawValue();
16299         
16300         if(this.tickable && this.editable){
16301             v = this.tickableInputEl().getValue();
16302         }
16303         
16304         this.doQuery(v);
16305     },
16306
16307     // private
16308     doForce : function(){
16309         if(this.inputEl().dom.value.length > 0){
16310             this.inputEl().dom.value =
16311                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16312              
16313         }
16314     },
16315
16316     /**
16317      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16318      * query allowing the query action to be canceled if needed.
16319      * @param {String} query The SQL query to execute
16320      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16321      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16322      * saved in the current store (defaults to false)
16323      */
16324     doQuery : function(q, forceAll){
16325         
16326         if(q === undefined || q === null){
16327             q = '';
16328         }
16329         var qe = {
16330             query: q,
16331             forceAll: forceAll,
16332             combo: this,
16333             cancel:false
16334         };
16335         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16336             return false;
16337         }
16338         q = qe.query;
16339         
16340         forceAll = qe.forceAll;
16341         if(forceAll === true || (q.length >= this.minChars)){
16342             
16343             this.hasQuery = true;
16344             
16345             if(this.lastQuery != q || this.alwaysQuery){
16346                 this.lastQuery = q;
16347                 if(this.mode == 'local'){
16348                     this.selectedIndex = -1;
16349                     if(forceAll){
16350                         this.store.clearFilter();
16351                     }else{
16352                         
16353                         if(this.specialFilter){
16354                             this.fireEvent('specialfilter', this);
16355                             this.onLoad();
16356                             return;
16357                         }
16358                         
16359                         this.store.filter(this.displayField, q);
16360                     }
16361                     
16362                     this.store.fireEvent("datachanged", this.store);
16363                     
16364                     this.onLoad();
16365                     
16366                     
16367                 }else{
16368                     
16369                     this.store.baseParams[this.queryParam] = q;
16370                     
16371                     var options = {params : this.getParams(q)};
16372                     
16373                     if(this.loadNext){
16374                         options.add = true;
16375                         options.params.start = this.page * this.pageSize;
16376                     }
16377                     
16378                     this.store.load(options);
16379                     
16380                     /*
16381                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16382                      *  we should expand the list on onLoad
16383                      *  so command out it
16384                      */
16385 //                    this.expand();
16386                 }
16387             }else{
16388                 this.selectedIndex = -1;
16389                 this.onLoad();   
16390             }
16391         }
16392         
16393         this.loadNext = false;
16394     },
16395     
16396     // private
16397     getParams : function(q){
16398         var p = {};
16399         //p[this.queryParam] = q;
16400         
16401         if(this.pageSize){
16402             p.start = 0;
16403             p.limit = this.pageSize;
16404         }
16405         return p;
16406     },
16407
16408     /**
16409      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16410      */
16411     collapse : function(){
16412         if(!this.isExpanded()){
16413             return;
16414         }
16415         
16416         this.list.hide();
16417         
16418         this.hasFocus = false;
16419         
16420         if(this.tickable){
16421             this.okBtn.hide();
16422             this.cancelBtn.hide();
16423             this.trigger.show();
16424             
16425             if(this.editable){
16426                 this.tickableInputEl().dom.value = '';
16427                 this.tickableInputEl().blur();
16428             }
16429             
16430         }
16431         
16432         Roo.get(document).un('mousedown', this.collapseIf, this);
16433         Roo.get(document).un('mousewheel', this.collapseIf, this);
16434         if (!this.editable) {
16435             Roo.get(document).un('keydown', this.listKeyPress, this);
16436         }
16437         this.fireEvent('collapse', this);
16438         
16439         this.validate();
16440     },
16441
16442     // private
16443     collapseIf : function(e){
16444         var in_combo  = e.within(this.el);
16445         var in_list =  e.within(this.list);
16446         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16447         
16448         if (in_combo || in_list || is_list) {
16449             //e.stopPropagation();
16450             return;
16451         }
16452         
16453         if(this.tickable){
16454             this.onTickableFooterButtonClick(e, false, false);
16455         }
16456
16457         this.collapse();
16458         
16459     },
16460
16461     /**
16462      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16463      */
16464     expand : function(){
16465        
16466         if(this.isExpanded() || !this.hasFocus){
16467             return;
16468         }
16469         
16470         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16471         this.list.setWidth(lw);
16472         
16473         Roo.log('expand');
16474         
16475         this.list.show();
16476         
16477         this.restrictHeight();
16478         
16479         if(this.tickable){
16480             
16481             this.tickItems = Roo.apply([], this.item);
16482             
16483             this.okBtn.show();
16484             this.cancelBtn.show();
16485             this.trigger.hide();
16486             
16487             if(this.editable){
16488                 this.tickableInputEl().focus();
16489             }
16490             
16491         }
16492         
16493         Roo.get(document).on('mousedown', this.collapseIf, this);
16494         Roo.get(document).on('mousewheel', this.collapseIf, this);
16495         if (!this.editable) {
16496             Roo.get(document).on('keydown', this.listKeyPress, this);
16497         }
16498         
16499         this.fireEvent('expand', this);
16500     },
16501
16502     // private
16503     // Implements the default empty TriggerField.onTriggerClick function
16504     onTriggerClick : function(e)
16505     {
16506         Roo.log('trigger click');
16507         
16508         if(this.disabled || !this.triggerList){
16509             return;
16510         }
16511         
16512         this.page = 0;
16513         this.loadNext = false;
16514         
16515         if(this.isExpanded()){
16516             this.collapse();
16517             if (!this.blockFocus) {
16518                 this.inputEl().focus();
16519             }
16520             
16521         }else {
16522             this.hasFocus = true;
16523             if(this.triggerAction == 'all') {
16524                 this.doQuery(this.allQuery, true);
16525             } else {
16526                 this.doQuery(this.getRawValue());
16527             }
16528             if (!this.blockFocus) {
16529                 this.inputEl().focus();
16530             }
16531         }
16532     },
16533     
16534     onTickableTriggerClick : function(e)
16535     {
16536         if(this.disabled){
16537             return;
16538         }
16539         
16540         this.page = 0;
16541         this.loadNext = false;
16542         this.hasFocus = true;
16543         
16544         if(this.triggerAction == 'all') {
16545             this.doQuery(this.allQuery, true);
16546         } else {
16547             this.doQuery(this.getRawValue());
16548         }
16549     },
16550     
16551     onSearchFieldClick : function(e)
16552     {
16553         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16554             this.onTickableFooterButtonClick(e, false, false);
16555             return;
16556         }
16557         
16558         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16559             return;
16560         }
16561         
16562         this.page = 0;
16563         this.loadNext = false;
16564         this.hasFocus = true;
16565         
16566         if(this.triggerAction == 'all') {
16567             this.doQuery(this.allQuery, true);
16568         } else {
16569             this.doQuery(this.getRawValue());
16570         }
16571     },
16572     
16573     listKeyPress : function(e)
16574     {
16575         //Roo.log('listkeypress');
16576         // scroll to first matching element based on key pres..
16577         if (e.isSpecialKey()) {
16578             return false;
16579         }
16580         var k = String.fromCharCode(e.getKey()).toUpperCase();
16581         //Roo.log(k);
16582         var match  = false;
16583         var csel = this.view.getSelectedNodes();
16584         var cselitem = false;
16585         if (csel.length) {
16586             var ix = this.view.indexOf(csel[0]);
16587             cselitem  = this.store.getAt(ix);
16588             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16589                 cselitem = false;
16590             }
16591             
16592         }
16593         
16594         this.store.each(function(v) { 
16595             if (cselitem) {
16596                 // start at existing selection.
16597                 if (cselitem.id == v.id) {
16598                     cselitem = false;
16599                 }
16600                 return true;
16601             }
16602                 
16603             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16604                 match = this.store.indexOf(v);
16605                 return false;
16606             }
16607             return true;
16608         }, this);
16609         
16610         if (match === false) {
16611             return true; // no more action?
16612         }
16613         // scroll to?
16614         this.view.select(match);
16615         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16616         sn.scrollIntoView(sn.dom.parentNode, false);
16617     },
16618     
16619     onViewScroll : function(e, t){
16620         
16621         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){
16622             return;
16623         }
16624         
16625         this.hasQuery = true;
16626         
16627         this.loading = this.list.select('.loading', true).first();
16628         
16629         if(this.loading === null){
16630             this.list.createChild({
16631                 tag: 'div',
16632                 cls: 'loading roo-select2-more-results roo-select2-active',
16633                 html: 'Loading more results...'
16634             });
16635             
16636             this.loading = this.list.select('.loading', true).first();
16637             
16638             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16639             
16640             this.loading.hide();
16641         }
16642         
16643         this.loading.show();
16644         
16645         var _combo = this;
16646         
16647         this.page++;
16648         this.loadNext = true;
16649         
16650         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16651         
16652         return;
16653     },
16654     
16655     addItem : function(o)
16656     {   
16657         var dv = ''; // display value
16658         
16659         if (this.displayField) {
16660             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16661         } else {
16662             // this is an error condition!!!
16663             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16664         }
16665         
16666         if(!dv.length){
16667             return;
16668         }
16669         
16670         var choice = this.choices.createChild({
16671             tag: 'li',
16672             cls: 'roo-select2-search-choice',
16673             cn: [
16674                 {
16675                     tag: 'div',
16676                     html: dv
16677                 },
16678                 {
16679                     tag: 'a',
16680                     href: '#',
16681                     cls: 'roo-select2-search-choice-close fa fa-times',
16682                     tabindex: '-1'
16683                 }
16684             ]
16685             
16686         }, this.searchField);
16687         
16688         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16689         
16690         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16691         
16692         this.item.push(o);
16693         
16694         this.lastData = o;
16695         
16696         this.syncValue();
16697         
16698         this.inputEl().dom.value = '';
16699         
16700         this.validate();
16701     },
16702     
16703     onRemoveItem : function(e, _self, o)
16704     {
16705         e.preventDefault();
16706         
16707         this.lastItem = Roo.apply([], this.item);
16708         
16709         var index = this.item.indexOf(o.data) * 1;
16710         
16711         if( index < 0){
16712             Roo.log('not this item?!');
16713             return;
16714         }
16715         
16716         this.item.splice(index, 1);
16717         o.item.remove();
16718         
16719         this.syncValue();
16720         
16721         this.fireEvent('remove', this, e);
16722         
16723         this.validate();
16724         
16725     },
16726     
16727     syncValue : function()
16728     {
16729         if(!this.item.length){
16730             this.clearValue();
16731             return;
16732         }
16733             
16734         var value = [];
16735         var _this = this;
16736         Roo.each(this.item, function(i){
16737             if(_this.valueField){
16738                 value.push(i[_this.valueField]);
16739                 return;
16740             }
16741
16742             value.push(i);
16743         });
16744
16745         this.value = value.join(',');
16746
16747         if(this.hiddenField){
16748             this.hiddenField.dom.value = this.value;
16749         }
16750         
16751         this.store.fireEvent("datachanged", this.store);
16752         
16753         this.validate();
16754     },
16755     
16756     clearItem : function()
16757     {
16758         if(!this.multiple){
16759             return;
16760         }
16761         
16762         this.item = [];
16763         
16764         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16765            c.remove();
16766         });
16767         
16768         this.syncValue();
16769         
16770         this.validate();
16771         
16772         if(this.tickable && !Roo.isTouch){
16773             this.view.refresh();
16774         }
16775     },
16776     
16777     inputEl: function ()
16778     {
16779         if(Roo.isIOS && this.useNativeIOS){
16780             return this.el.select('select.roo-ios-select', true).first();
16781         }
16782         
16783         if(Roo.isTouch && this.mobileTouchView){
16784             return this.el.select('input.form-control',true).first();
16785         }
16786         
16787         if(this.tickable){
16788             return this.searchField;
16789         }
16790         
16791         return this.el.select('input.form-control',true).first();
16792     },
16793     
16794     onTickableFooterButtonClick : function(e, btn, el)
16795     {
16796         e.preventDefault();
16797         
16798         this.lastItem = Roo.apply([], this.item);
16799         
16800         if(btn && btn.name == 'cancel'){
16801             this.tickItems = Roo.apply([], this.item);
16802             this.collapse();
16803             return;
16804         }
16805         
16806         this.clearItem();
16807         
16808         var _this = this;
16809         
16810         Roo.each(this.tickItems, function(o){
16811             _this.addItem(o);
16812         });
16813         
16814         this.collapse();
16815         
16816     },
16817     
16818     validate : function()
16819     {
16820         if(this.getVisibilityEl().hasClass('hidden')){
16821             return true;
16822         }
16823         
16824         var v = this.getRawValue();
16825         
16826         if(this.multiple){
16827             v = this.getValue();
16828         }
16829         
16830         if(this.disabled || this.allowBlank || v.length){
16831             this.markValid();
16832             return true;
16833         }
16834         
16835         this.markInvalid();
16836         return false;
16837     },
16838     
16839     tickableInputEl : function()
16840     {
16841         if(!this.tickable || !this.editable){
16842             return this.inputEl();
16843         }
16844         
16845         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16846     },
16847     
16848     
16849     getAutoCreateTouchView : function()
16850     {
16851         var id = Roo.id();
16852         
16853         var cfg = {
16854             cls: 'form-group' //input-group
16855         };
16856         
16857         var input =  {
16858             tag: 'input',
16859             id : id,
16860             type : this.inputType,
16861             cls : 'form-control x-combo-noedit',
16862             autocomplete: 'new-password',
16863             placeholder : this.placeholder || '',
16864             readonly : true
16865         };
16866         
16867         if (this.name) {
16868             input.name = this.name;
16869         }
16870         
16871         if (this.size) {
16872             input.cls += ' input-' + this.size;
16873         }
16874         
16875         if (this.disabled) {
16876             input.disabled = true;
16877         }
16878         
16879         var inputblock = {
16880             cls : '',
16881             cn : [
16882                 input
16883             ]
16884         };
16885         
16886         if(this.before){
16887             inputblock.cls += ' input-group';
16888             
16889             inputblock.cn.unshift({
16890                 tag :'span',
16891                 cls : 'input-group-addon input-group-prepend input-group-text',
16892                 html : this.before
16893             });
16894         }
16895         
16896         if(this.removable && !this.multiple){
16897             inputblock.cls += ' roo-removable';
16898             
16899             inputblock.cn.push({
16900                 tag: 'button',
16901                 html : 'x',
16902                 cls : 'roo-combo-removable-btn close'
16903             });
16904         }
16905
16906         if(this.hasFeedback && !this.allowBlank){
16907             
16908             inputblock.cls += ' has-feedback';
16909             
16910             inputblock.cn.push({
16911                 tag: 'span',
16912                 cls: 'glyphicon form-control-feedback'
16913             });
16914             
16915         }
16916         
16917         if (this.after) {
16918             
16919             inputblock.cls += (this.before) ? '' : ' input-group';
16920             
16921             inputblock.cn.push({
16922                 tag :'span',
16923                 cls : 'input-group-addon input-group-append input-group-text',
16924                 html : this.after
16925             });
16926         }
16927
16928         
16929         var ibwrap = inputblock;
16930         
16931         if(this.multiple){
16932             ibwrap = {
16933                 tag: 'ul',
16934                 cls: 'roo-select2-choices',
16935                 cn:[
16936                     {
16937                         tag: 'li',
16938                         cls: 'roo-select2-search-field',
16939                         cn: [
16940
16941                             inputblock
16942                         ]
16943                     }
16944                 ]
16945             };
16946         
16947             
16948         }
16949         
16950         var combobox = {
16951             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16952             cn: [
16953                 {
16954                     tag: 'input',
16955                     type : 'hidden',
16956                     cls: 'form-hidden-field'
16957                 },
16958                 ibwrap
16959             ]
16960         };
16961         
16962         if(!this.multiple && this.showToggleBtn){
16963             
16964             var caret = {
16965                 cls: 'caret'
16966             };
16967             
16968             if (this.caret != false) {
16969                 caret = {
16970                      tag: 'i',
16971                      cls: 'fa fa-' + this.caret
16972                 };
16973                 
16974             }
16975             
16976             combobox.cn.push({
16977                 tag :'span',
16978                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16979                 cn : [
16980                     Roo.bootstrap.version == 3 ? caret : '',
16981                     {
16982                         tag: 'span',
16983                         cls: 'combobox-clear',
16984                         cn  : [
16985                             {
16986                                 tag : 'i',
16987                                 cls: 'icon-remove'
16988                             }
16989                         ]
16990                     }
16991                 ]
16992
16993             })
16994         }
16995         
16996         if(this.multiple){
16997             combobox.cls += ' roo-select2-container-multi';
16998         }
16999         
17000         var align = this.labelAlign || this.parentLabelAlign();
17001         
17002         if (align ==='left' && this.fieldLabel.length) {
17003
17004             cfg.cn = [
17005                 {
17006                    tag : 'i',
17007                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17008                    tooltip : 'This field is required'
17009                 },
17010                 {
17011                     tag: 'label',
17012                     cls : 'control-label col-form-label',
17013                     html : this.fieldLabel
17014
17015                 },
17016                 {
17017                     cls : '', 
17018                     cn: [
17019                         combobox
17020                     ]
17021                 }
17022             ];
17023             
17024             var labelCfg = cfg.cn[1];
17025             var contentCfg = cfg.cn[2];
17026             
17027
17028             if(this.indicatorpos == 'right'){
17029                 cfg.cn = [
17030                     {
17031                         tag: 'label',
17032                         'for' :  id,
17033                         cls : 'control-label col-form-label',
17034                         cn : [
17035                             {
17036                                 tag : 'span',
17037                                 html : this.fieldLabel
17038                             },
17039                             {
17040                                 tag : 'i',
17041                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17042                                 tooltip : 'This field is required'
17043                             }
17044                         ]
17045                     },
17046                     {
17047                         cls : "",
17048                         cn: [
17049                             combobox
17050                         ]
17051                     }
17052
17053                 ];
17054                 
17055                 labelCfg = cfg.cn[0];
17056                 contentCfg = cfg.cn[1];
17057             }
17058             
17059            
17060             
17061             if(this.labelWidth > 12){
17062                 labelCfg.style = "width: " + this.labelWidth + 'px';
17063             }
17064             
17065             if(this.labelWidth < 13 && this.labelmd == 0){
17066                 this.labelmd = this.labelWidth;
17067             }
17068             
17069             if(this.labellg > 0){
17070                 labelCfg.cls += ' col-lg-' + this.labellg;
17071                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17072             }
17073             
17074             if(this.labelmd > 0){
17075                 labelCfg.cls += ' col-md-' + this.labelmd;
17076                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17077             }
17078             
17079             if(this.labelsm > 0){
17080                 labelCfg.cls += ' col-sm-' + this.labelsm;
17081                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17082             }
17083             
17084             if(this.labelxs > 0){
17085                 labelCfg.cls += ' col-xs-' + this.labelxs;
17086                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17087             }
17088                 
17089                 
17090         } else if ( this.fieldLabel.length) {
17091             cfg.cn = [
17092                 {
17093                    tag : 'i',
17094                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17095                    tooltip : 'This field is required'
17096                 },
17097                 {
17098                     tag: 'label',
17099                     cls : 'control-label',
17100                     html : this.fieldLabel
17101
17102                 },
17103                 {
17104                     cls : '', 
17105                     cn: [
17106                         combobox
17107                     ]
17108                 }
17109             ];
17110             
17111             if(this.indicatorpos == 'right'){
17112                 cfg.cn = [
17113                     {
17114                         tag: 'label',
17115                         cls : 'control-label',
17116                         html : this.fieldLabel,
17117                         cn : [
17118                             {
17119                                tag : 'i',
17120                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17121                                tooltip : 'This field is required'
17122                             }
17123                         ]
17124                     },
17125                     {
17126                         cls : '', 
17127                         cn: [
17128                             combobox
17129                         ]
17130                     }
17131                 ];
17132             }
17133         } else {
17134             cfg.cn = combobox;    
17135         }
17136         
17137         
17138         var settings = this;
17139         
17140         ['xs','sm','md','lg'].map(function(size){
17141             if (settings[size]) {
17142                 cfg.cls += ' col-' + size + '-' + settings[size];
17143             }
17144         });
17145         
17146         return cfg;
17147     },
17148     
17149     initTouchView : function()
17150     {
17151         this.renderTouchView();
17152         
17153         this.touchViewEl.on('scroll', function(){
17154             this.el.dom.scrollTop = 0;
17155         }, this);
17156         
17157         this.originalValue = this.getValue();
17158         
17159         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17160         
17161         this.inputEl().on("click", this.showTouchView, this);
17162         if (this.triggerEl) {
17163             this.triggerEl.on("click", this.showTouchView, this);
17164         }
17165         
17166         
17167         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17168         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17169         
17170         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17171         
17172         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17173         this.store.on('load', this.onTouchViewLoad, this);
17174         this.store.on('loadexception', this.onTouchViewLoadException, this);
17175         
17176         if(this.hiddenName){
17177             
17178             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17179             
17180             this.hiddenField.dom.value =
17181                 this.hiddenValue !== undefined ? this.hiddenValue :
17182                 this.value !== undefined ? this.value : '';
17183         
17184             this.el.dom.removeAttribute('name');
17185             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17186         }
17187         
17188         if(this.multiple){
17189             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17190             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17191         }
17192         
17193         if(this.removable && !this.multiple){
17194             var close = this.closeTriggerEl();
17195             if(close){
17196                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17197                 close.on('click', this.removeBtnClick, this, close);
17198             }
17199         }
17200         /*
17201          * fix the bug in Safari iOS8
17202          */
17203         this.inputEl().on("focus", function(e){
17204             document.activeElement.blur();
17205         }, this);
17206         
17207         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17208         
17209         return;
17210         
17211         
17212     },
17213     
17214     renderTouchView : function()
17215     {
17216         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17217         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17218         
17219         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17220         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17221         
17222         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17223         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17224         this.touchViewBodyEl.setStyle('overflow', 'auto');
17225         
17226         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17227         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17228         
17229         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17230         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17231         
17232     },
17233     
17234     showTouchView : function()
17235     {
17236         if(this.disabled){
17237             return;
17238         }
17239         
17240         this.touchViewHeaderEl.hide();
17241
17242         if(this.modalTitle.length){
17243             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17244             this.touchViewHeaderEl.show();
17245         }
17246
17247         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17248         this.touchViewEl.show();
17249
17250         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17251         
17252         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17253         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17254
17255         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17256
17257         if(this.modalTitle.length){
17258             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17259         }
17260         
17261         this.touchViewBodyEl.setHeight(bodyHeight);
17262
17263         if(this.animate){
17264             var _this = this;
17265             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17266         }else{
17267             this.touchViewEl.addClass('in');
17268         }
17269         
17270         if(this._touchViewMask){
17271             Roo.get(document.body).addClass("x-body-masked");
17272             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17273             this._touchViewMask.setStyle('z-index', 10000);
17274             this._touchViewMask.addClass('show');
17275         }
17276         
17277         this.doTouchViewQuery();
17278         
17279     },
17280     
17281     hideTouchView : function()
17282     {
17283         this.touchViewEl.removeClass('in');
17284
17285         if(this.animate){
17286             var _this = this;
17287             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17288         }else{
17289             this.touchViewEl.setStyle('display', 'none');
17290         }
17291         
17292         if(this._touchViewMask){
17293             this._touchViewMask.removeClass('show');
17294             Roo.get(document.body).removeClass("x-body-masked");
17295         }
17296     },
17297     
17298     setTouchViewValue : function()
17299     {
17300         if(this.multiple){
17301             this.clearItem();
17302         
17303             var _this = this;
17304
17305             Roo.each(this.tickItems, function(o){
17306                 this.addItem(o);
17307             }, this);
17308         }
17309         
17310         this.hideTouchView();
17311     },
17312     
17313     doTouchViewQuery : function()
17314     {
17315         var qe = {
17316             query: '',
17317             forceAll: true,
17318             combo: this,
17319             cancel:false
17320         };
17321         
17322         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17323             return false;
17324         }
17325         
17326         if(!this.alwaysQuery || this.mode == 'local'){
17327             this.onTouchViewLoad();
17328             return;
17329         }
17330         
17331         this.store.load();
17332     },
17333     
17334     onTouchViewBeforeLoad : function(combo,opts)
17335     {
17336         return;
17337     },
17338
17339     // private
17340     onTouchViewLoad : function()
17341     {
17342         if(this.store.getCount() < 1){
17343             this.onTouchViewEmptyResults();
17344             return;
17345         }
17346         
17347         this.clearTouchView();
17348         
17349         var rawValue = this.getRawValue();
17350         
17351         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17352         
17353         this.tickItems = [];
17354         
17355         this.store.data.each(function(d, rowIndex){
17356             var row = this.touchViewListGroup.createChild(template);
17357             
17358             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17359                 row.addClass(d.data.cls);
17360             }
17361             
17362             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17363                 var cfg = {
17364                     data : d.data,
17365                     html : d.data[this.displayField]
17366                 };
17367                 
17368                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17369                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17370                 }
17371             }
17372             row.removeClass('selected');
17373             if(!this.multiple && this.valueField &&
17374                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17375             {
17376                 // radio buttons..
17377                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17378                 row.addClass('selected');
17379             }
17380             
17381             if(this.multiple && this.valueField &&
17382                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17383             {
17384                 
17385                 // checkboxes...
17386                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17387                 this.tickItems.push(d.data);
17388             }
17389             
17390             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17391             
17392         }, this);
17393         
17394         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17395         
17396         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17397
17398         if(this.modalTitle.length){
17399             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17400         }
17401
17402         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17403         
17404         if(this.mobile_restrict_height && listHeight < bodyHeight){
17405             this.touchViewBodyEl.setHeight(listHeight);
17406         }
17407         
17408         var _this = this;
17409         
17410         if(firstChecked && listHeight > bodyHeight){
17411             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17412         }
17413         
17414     },
17415     
17416     onTouchViewLoadException : function()
17417     {
17418         this.hideTouchView();
17419     },
17420     
17421     onTouchViewEmptyResults : function()
17422     {
17423         this.clearTouchView();
17424         
17425         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17426         
17427         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17428         
17429     },
17430     
17431     clearTouchView : function()
17432     {
17433         this.touchViewListGroup.dom.innerHTML = '';
17434     },
17435     
17436     onTouchViewClick : function(e, el, o)
17437     {
17438         e.preventDefault();
17439         
17440         var row = o.row;
17441         var rowIndex = o.rowIndex;
17442         
17443         var r = this.store.getAt(rowIndex);
17444         
17445         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17446             
17447             if(!this.multiple){
17448                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17449                     c.dom.removeAttribute('checked');
17450                 }, this);
17451
17452                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17453
17454                 this.setFromData(r.data);
17455
17456                 var close = this.closeTriggerEl();
17457
17458                 if(close){
17459                     close.show();
17460                 }
17461
17462                 this.hideTouchView();
17463
17464                 this.fireEvent('select', this, r, rowIndex);
17465
17466                 return;
17467             }
17468
17469             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17470                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17471                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17472                 return;
17473             }
17474
17475             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17476             this.addItem(r.data);
17477             this.tickItems.push(r.data);
17478         }
17479     },
17480     
17481     getAutoCreateNativeIOS : function()
17482     {
17483         var cfg = {
17484             cls: 'form-group' //input-group,
17485         };
17486         
17487         var combobox =  {
17488             tag: 'select',
17489             cls : 'roo-ios-select'
17490         };
17491         
17492         if (this.name) {
17493             combobox.name = this.name;
17494         }
17495         
17496         if (this.disabled) {
17497             combobox.disabled = true;
17498         }
17499         
17500         var settings = this;
17501         
17502         ['xs','sm','md','lg'].map(function(size){
17503             if (settings[size]) {
17504                 cfg.cls += ' col-' + size + '-' + settings[size];
17505             }
17506         });
17507         
17508         cfg.cn = combobox;
17509         
17510         return cfg;
17511         
17512     },
17513     
17514     initIOSView : function()
17515     {
17516         this.store.on('load', this.onIOSViewLoad, this);
17517         
17518         return;
17519     },
17520     
17521     onIOSViewLoad : function()
17522     {
17523         if(this.store.getCount() < 1){
17524             return;
17525         }
17526         
17527         this.clearIOSView();
17528         
17529         if(this.allowBlank) {
17530             
17531             var default_text = '-- SELECT --';
17532             
17533             if(this.placeholder.length){
17534                 default_text = this.placeholder;
17535             }
17536             
17537             if(this.emptyTitle.length){
17538                 default_text += ' - ' + this.emptyTitle + ' -';
17539             }
17540             
17541             var opt = this.inputEl().createChild({
17542                 tag: 'option',
17543                 value : 0,
17544                 html : default_text
17545             });
17546             
17547             var o = {};
17548             o[this.valueField] = 0;
17549             o[this.displayField] = default_text;
17550             
17551             this.ios_options.push({
17552                 data : o,
17553                 el : opt
17554             });
17555             
17556         }
17557         
17558         this.store.data.each(function(d, rowIndex){
17559             
17560             var html = '';
17561             
17562             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17563                 html = d.data[this.displayField];
17564             }
17565             
17566             var value = '';
17567             
17568             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17569                 value = d.data[this.valueField];
17570             }
17571             
17572             var option = {
17573                 tag: 'option',
17574                 value : value,
17575                 html : html
17576             };
17577             
17578             if(this.value == d.data[this.valueField]){
17579                 option['selected'] = true;
17580             }
17581             
17582             var opt = this.inputEl().createChild(option);
17583             
17584             this.ios_options.push({
17585                 data : d.data,
17586                 el : opt
17587             });
17588             
17589         }, this);
17590         
17591         this.inputEl().on('change', function(){
17592            this.fireEvent('select', this);
17593         }, this);
17594         
17595     },
17596     
17597     clearIOSView: function()
17598     {
17599         this.inputEl().dom.innerHTML = '';
17600         
17601         this.ios_options = [];
17602     },
17603     
17604     setIOSValue: function(v)
17605     {
17606         this.value = v;
17607         
17608         if(!this.ios_options){
17609             return;
17610         }
17611         
17612         Roo.each(this.ios_options, function(opts){
17613            
17614            opts.el.dom.removeAttribute('selected');
17615            
17616            if(opts.data[this.valueField] != v){
17617                return;
17618            }
17619            
17620            opts.el.dom.setAttribute('selected', true);
17621            
17622         }, this);
17623     }
17624
17625     /** 
17626     * @cfg {Boolean} grow 
17627     * @hide 
17628     */
17629     /** 
17630     * @cfg {Number} growMin 
17631     * @hide 
17632     */
17633     /** 
17634     * @cfg {Number} growMax 
17635     * @hide 
17636     */
17637     /**
17638      * @hide
17639      * @method autoSize
17640      */
17641 });
17642
17643 Roo.apply(Roo.bootstrap.ComboBox,  {
17644     
17645     header : {
17646         tag: 'div',
17647         cls: 'modal-header',
17648         cn: [
17649             {
17650                 tag: 'h4',
17651                 cls: 'modal-title'
17652             }
17653         ]
17654     },
17655     
17656     body : {
17657         tag: 'div',
17658         cls: 'modal-body',
17659         cn: [
17660             {
17661                 tag: 'ul',
17662                 cls: 'list-group'
17663             }
17664         ]
17665     },
17666     
17667     listItemRadio : {
17668         tag: 'li',
17669         cls: 'list-group-item',
17670         cn: [
17671             {
17672                 tag: 'span',
17673                 cls: 'roo-combobox-list-group-item-value'
17674             },
17675             {
17676                 tag: 'div',
17677                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17678                 cn: [
17679                     {
17680                         tag: 'input',
17681                         type: 'radio'
17682                     },
17683                     {
17684                         tag: 'label'
17685                     }
17686                 ]
17687             }
17688         ]
17689     },
17690     
17691     listItemCheckbox : {
17692         tag: 'li',
17693         cls: 'list-group-item',
17694         cn: [
17695             {
17696                 tag: 'span',
17697                 cls: 'roo-combobox-list-group-item-value'
17698             },
17699             {
17700                 tag: 'div',
17701                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17702                 cn: [
17703                     {
17704                         tag: 'input',
17705                         type: 'checkbox'
17706                     },
17707                     {
17708                         tag: 'label'
17709                     }
17710                 ]
17711             }
17712         ]
17713     },
17714     
17715     emptyResult : {
17716         tag: 'div',
17717         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17718     },
17719     
17720     footer : {
17721         tag: 'div',
17722         cls: 'modal-footer',
17723         cn: [
17724             {
17725                 tag: 'div',
17726                 cls: 'row',
17727                 cn: [
17728                     {
17729                         tag: 'div',
17730                         cls: 'col-xs-6 text-left',
17731                         cn: {
17732                             tag: 'button',
17733                             cls: 'btn btn-danger roo-touch-view-cancel',
17734                             html: 'Cancel'
17735                         }
17736                     },
17737                     {
17738                         tag: 'div',
17739                         cls: 'col-xs-6 text-right',
17740                         cn: {
17741                             tag: 'button',
17742                             cls: 'btn btn-success roo-touch-view-ok',
17743                             html: 'OK'
17744                         }
17745                     }
17746                 ]
17747             }
17748         ]
17749         
17750     }
17751 });
17752
17753 Roo.apply(Roo.bootstrap.ComboBox,  {
17754     
17755     touchViewTemplate : {
17756         tag: 'div',
17757         cls: 'modal fade roo-combobox-touch-view',
17758         cn: [
17759             {
17760                 tag: 'div',
17761                 cls: 'modal-dialog',
17762                 style : 'position:fixed', // we have to fix position....
17763                 cn: [
17764                     {
17765                         tag: 'div',
17766                         cls: 'modal-content',
17767                         cn: [
17768                             Roo.bootstrap.ComboBox.header,
17769                             Roo.bootstrap.ComboBox.body,
17770                             Roo.bootstrap.ComboBox.footer
17771                         ]
17772                     }
17773                 ]
17774             }
17775         ]
17776     }
17777 });/*
17778  * Based on:
17779  * Ext JS Library 1.1.1
17780  * Copyright(c) 2006-2007, Ext JS, LLC.
17781  *
17782  * Originally Released Under LGPL - original licence link has changed is not relivant.
17783  *
17784  * Fork - LGPL
17785  * <script type="text/javascript">
17786  */
17787
17788 /**
17789  * @class Roo.View
17790  * @extends Roo.util.Observable
17791  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17792  * This class also supports single and multi selection modes. <br>
17793  * Create a data model bound view:
17794  <pre><code>
17795  var store = new Roo.data.Store(...);
17796
17797  var view = new Roo.View({
17798     el : "my-element",
17799     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17800  
17801     singleSelect: true,
17802     selectedClass: "ydataview-selected",
17803     store: store
17804  });
17805
17806  // listen for node click?
17807  view.on("click", function(vw, index, node, e){
17808  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17809  });
17810
17811  // load XML data
17812  dataModel.load("foobar.xml");
17813  </code></pre>
17814  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17815  * <br><br>
17816  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17817  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17818  * 
17819  * Note: old style constructor is still suported (container, template, config)
17820  * 
17821  * @constructor
17822  * Create a new View
17823  * @param {Object} config The config object
17824  * 
17825  */
17826 Roo.View = function(config, depreciated_tpl, depreciated_config){
17827     
17828     this.parent = false;
17829     
17830     if (typeof(depreciated_tpl) == 'undefined') {
17831         // new way.. - universal constructor.
17832         Roo.apply(this, config);
17833         this.el  = Roo.get(this.el);
17834     } else {
17835         // old format..
17836         this.el  = Roo.get(config);
17837         this.tpl = depreciated_tpl;
17838         Roo.apply(this, depreciated_config);
17839     }
17840     this.wrapEl  = this.el.wrap().wrap();
17841     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17842     
17843     
17844     if(typeof(this.tpl) == "string"){
17845         this.tpl = new Roo.Template(this.tpl);
17846     } else {
17847         // support xtype ctors..
17848         this.tpl = new Roo.factory(this.tpl, Roo);
17849     }
17850     
17851     
17852     this.tpl.compile();
17853     
17854     /** @private */
17855     this.addEvents({
17856         /**
17857          * @event beforeclick
17858          * Fires before a click is processed. Returns false to cancel the default action.
17859          * @param {Roo.View} this
17860          * @param {Number} index The index of the target node
17861          * @param {HTMLElement} node The target node
17862          * @param {Roo.EventObject} e The raw event object
17863          */
17864             "beforeclick" : true,
17865         /**
17866          * @event click
17867          * Fires when a template node is clicked.
17868          * @param {Roo.View} this
17869          * @param {Number} index The index of the target node
17870          * @param {HTMLElement} node The target node
17871          * @param {Roo.EventObject} e The raw event object
17872          */
17873             "click" : true,
17874         /**
17875          * @event dblclick
17876          * Fires when a template node is double clicked.
17877          * @param {Roo.View} this
17878          * @param {Number} index The index of the target node
17879          * @param {HTMLElement} node The target node
17880          * @param {Roo.EventObject} e The raw event object
17881          */
17882             "dblclick" : true,
17883         /**
17884          * @event contextmenu
17885          * Fires when a template node is right clicked.
17886          * @param {Roo.View} this
17887          * @param {Number} index The index of the target node
17888          * @param {HTMLElement} node The target node
17889          * @param {Roo.EventObject} e The raw event object
17890          */
17891             "contextmenu" : true,
17892         /**
17893          * @event selectionchange
17894          * Fires when the selected nodes change.
17895          * @param {Roo.View} this
17896          * @param {Array} selections Array of the selected nodes
17897          */
17898             "selectionchange" : true,
17899     
17900         /**
17901          * @event beforeselect
17902          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17903          * @param {Roo.View} this
17904          * @param {HTMLElement} node The node to be selected
17905          * @param {Array} selections Array of currently selected nodes
17906          */
17907             "beforeselect" : true,
17908         /**
17909          * @event preparedata
17910          * Fires on every row to render, to allow you to change the data.
17911          * @param {Roo.View} this
17912          * @param {Object} data to be rendered (change this)
17913          */
17914           "preparedata" : true
17915           
17916           
17917         });
17918
17919
17920
17921     this.el.on({
17922         "click": this.onClick,
17923         "dblclick": this.onDblClick,
17924         "contextmenu": this.onContextMenu,
17925         scope:this
17926     });
17927
17928     this.selections = [];
17929     this.nodes = [];
17930     this.cmp = new Roo.CompositeElementLite([]);
17931     if(this.store){
17932         this.store = Roo.factory(this.store, Roo.data);
17933         this.setStore(this.store, true);
17934     }
17935     
17936     if ( this.footer && this.footer.xtype) {
17937            
17938          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17939         
17940         this.footer.dataSource = this.store;
17941         this.footer.container = fctr;
17942         this.footer = Roo.factory(this.footer, Roo);
17943         fctr.insertFirst(this.el);
17944         
17945         // this is a bit insane - as the paging toolbar seems to detach the el..
17946 //        dom.parentNode.parentNode.parentNode
17947          // they get detached?
17948     }
17949     
17950     
17951     Roo.View.superclass.constructor.call(this);
17952     
17953     
17954 };
17955
17956 Roo.extend(Roo.View, Roo.util.Observable, {
17957     
17958      /**
17959      * @cfg {Roo.data.Store} store Data store to load data from.
17960      */
17961     store : false,
17962     
17963     /**
17964      * @cfg {String|Roo.Element} el The container element.
17965      */
17966     el : '',
17967     
17968     /**
17969      * @cfg {String|Roo.Template} tpl The template used by this View 
17970      */
17971     tpl : false,
17972     /**
17973      * @cfg {String} dataName the named area of the template to use as the data area
17974      *                          Works with domtemplates roo-name="name"
17975      */
17976     dataName: false,
17977     /**
17978      * @cfg {String} selectedClass The css class to add to selected nodes
17979      */
17980     selectedClass : "x-view-selected",
17981      /**
17982      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17983      */
17984     emptyText : "",
17985     
17986     /**
17987      * @cfg {String} text to display on mask (default Loading)
17988      */
17989     mask : false,
17990     /**
17991      * @cfg {Boolean} multiSelect Allow multiple selection
17992      */
17993     multiSelect : false,
17994     /**
17995      * @cfg {Boolean} singleSelect Allow single selection
17996      */
17997     singleSelect:  false,
17998     
17999     /**
18000      * @cfg {Boolean} toggleSelect - selecting 
18001      */
18002     toggleSelect : false,
18003     
18004     /**
18005      * @cfg {Boolean} tickable - selecting 
18006      */
18007     tickable : false,
18008     
18009     /**
18010      * Returns the element this view is bound to.
18011      * @return {Roo.Element}
18012      */
18013     getEl : function(){
18014         return this.wrapEl;
18015     },
18016     
18017     
18018
18019     /**
18020      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18021      */
18022     refresh : function(){
18023         //Roo.log('refresh');
18024         var t = this.tpl;
18025         
18026         // if we are using something like 'domtemplate', then
18027         // the what gets used is:
18028         // t.applySubtemplate(NAME, data, wrapping data..)
18029         // the outer template then get' applied with
18030         //     the store 'extra data'
18031         // and the body get's added to the
18032         //      roo-name="data" node?
18033         //      <span class='roo-tpl-{name}'></span> ?????
18034         
18035         
18036         
18037         this.clearSelections();
18038         this.el.update("");
18039         var html = [];
18040         var records = this.store.getRange();
18041         if(records.length < 1) {
18042             
18043             // is this valid??  = should it render a template??
18044             
18045             this.el.update(this.emptyText);
18046             return;
18047         }
18048         var el = this.el;
18049         if (this.dataName) {
18050             this.el.update(t.apply(this.store.meta)); //????
18051             el = this.el.child('.roo-tpl-' + this.dataName);
18052         }
18053         
18054         for(var i = 0, len = records.length; i < len; i++){
18055             var data = this.prepareData(records[i].data, i, records[i]);
18056             this.fireEvent("preparedata", this, data, i, records[i]);
18057             
18058             var d = Roo.apply({}, data);
18059             
18060             if(this.tickable){
18061                 Roo.apply(d, {'roo-id' : Roo.id()});
18062                 
18063                 var _this = this;
18064             
18065                 Roo.each(this.parent.item, function(item){
18066                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18067                         return;
18068                     }
18069                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18070                 });
18071             }
18072             
18073             html[html.length] = Roo.util.Format.trim(
18074                 this.dataName ?
18075                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18076                     t.apply(d)
18077             );
18078         }
18079         
18080         
18081         
18082         el.update(html.join(""));
18083         this.nodes = el.dom.childNodes;
18084         this.updateIndexes(0);
18085     },
18086     
18087
18088     /**
18089      * Function to override to reformat the data that is sent to
18090      * the template for each node.
18091      * DEPRICATED - use the preparedata event handler.
18092      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18093      * a JSON object for an UpdateManager bound view).
18094      */
18095     prepareData : function(data, index, record)
18096     {
18097         this.fireEvent("preparedata", this, data, index, record);
18098         return data;
18099     },
18100
18101     onUpdate : function(ds, record){
18102         // Roo.log('on update');   
18103         this.clearSelections();
18104         var index = this.store.indexOf(record);
18105         var n = this.nodes[index];
18106         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18107         n.parentNode.removeChild(n);
18108         this.updateIndexes(index, index);
18109     },
18110
18111     
18112     
18113 // --------- FIXME     
18114     onAdd : function(ds, records, index)
18115     {
18116         //Roo.log(['on Add', ds, records, index] );        
18117         this.clearSelections();
18118         if(this.nodes.length == 0){
18119             this.refresh();
18120             return;
18121         }
18122         var n = this.nodes[index];
18123         for(var i = 0, len = records.length; i < len; i++){
18124             var d = this.prepareData(records[i].data, i, records[i]);
18125             if(n){
18126                 this.tpl.insertBefore(n, d);
18127             }else{
18128                 
18129                 this.tpl.append(this.el, d);
18130             }
18131         }
18132         this.updateIndexes(index);
18133     },
18134
18135     onRemove : function(ds, record, index){
18136        // Roo.log('onRemove');
18137         this.clearSelections();
18138         var el = this.dataName  ?
18139             this.el.child('.roo-tpl-' + this.dataName) :
18140             this.el; 
18141         
18142         el.dom.removeChild(this.nodes[index]);
18143         this.updateIndexes(index);
18144     },
18145
18146     /**
18147      * Refresh an individual node.
18148      * @param {Number} index
18149      */
18150     refreshNode : function(index){
18151         this.onUpdate(this.store, this.store.getAt(index));
18152     },
18153
18154     updateIndexes : function(startIndex, endIndex){
18155         var ns = this.nodes;
18156         startIndex = startIndex || 0;
18157         endIndex = endIndex || ns.length - 1;
18158         for(var i = startIndex; i <= endIndex; i++){
18159             ns[i].nodeIndex = i;
18160         }
18161     },
18162
18163     /**
18164      * Changes the data store this view uses and refresh the view.
18165      * @param {Store} store
18166      */
18167     setStore : function(store, initial){
18168         if(!initial && this.store){
18169             this.store.un("datachanged", this.refresh);
18170             this.store.un("add", this.onAdd);
18171             this.store.un("remove", this.onRemove);
18172             this.store.un("update", this.onUpdate);
18173             this.store.un("clear", this.refresh);
18174             this.store.un("beforeload", this.onBeforeLoad);
18175             this.store.un("load", this.onLoad);
18176             this.store.un("loadexception", this.onLoad);
18177         }
18178         if(store){
18179           
18180             store.on("datachanged", this.refresh, this);
18181             store.on("add", this.onAdd, this);
18182             store.on("remove", this.onRemove, this);
18183             store.on("update", this.onUpdate, this);
18184             store.on("clear", this.refresh, this);
18185             store.on("beforeload", this.onBeforeLoad, this);
18186             store.on("load", this.onLoad, this);
18187             store.on("loadexception", this.onLoad, this);
18188         }
18189         
18190         if(store){
18191             this.refresh();
18192         }
18193     },
18194     /**
18195      * onbeforeLoad - masks the loading area.
18196      *
18197      */
18198     onBeforeLoad : function(store,opts)
18199     {
18200          //Roo.log('onBeforeLoad');   
18201         if (!opts.add) {
18202             this.el.update("");
18203         }
18204         this.el.mask(this.mask ? this.mask : "Loading" ); 
18205     },
18206     onLoad : function ()
18207     {
18208         this.el.unmask();
18209     },
18210     
18211
18212     /**
18213      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18214      * @param {HTMLElement} node
18215      * @return {HTMLElement} The template node
18216      */
18217     findItemFromChild : function(node){
18218         var el = this.dataName  ?
18219             this.el.child('.roo-tpl-' + this.dataName,true) :
18220             this.el.dom; 
18221         
18222         if(!node || node.parentNode == el){
18223                     return node;
18224             }
18225             var p = node.parentNode;
18226             while(p && p != el){
18227             if(p.parentNode == el){
18228                 return p;
18229             }
18230             p = p.parentNode;
18231         }
18232             return null;
18233     },
18234
18235     /** @ignore */
18236     onClick : function(e){
18237         var item = this.findItemFromChild(e.getTarget());
18238         if(item){
18239             var index = this.indexOf(item);
18240             if(this.onItemClick(item, index, e) !== false){
18241                 this.fireEvent("click", this, index, item, e);
18242             }
18243         }else{
18244             this.clearSelections();
18245         }
18246     },
18247
18248     /** @ignore */
18249     onContextMenu : function(e){
18250         var item = this.findItemFromChild(e.getTarget());
18251         if(item){
18252             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18253         }
18254     },
18255
18256     /** @ignore */
18257     onDblClick : function(e){
18258         var item = this.findItemFromChild(e.getTarget());
18259         if(item){
18260             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18261         }
18262     },
18263
18264     onItemClick : function(item, index, e)
18265     {
18266         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18267             return false;
18268         }
18269         if (this.toggleSelect) {
18270             var m = this.isSelected(item) ? 'unselect' : 'select';
18271             //Roo.log(m);
18272             var _t = this;
18273             _t[m](item, true, false);
18274             return true;
18275         }
18276         if(this.multiSelect || this.singleSelect){
18277             if(this.multiSelect && e.shiftKey && this.lastSelection){
18278                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18279             }else{
18280                 this.select(item, this.multiSelect && e.ctrlKey);
18281                 this.lastSelection = item;
18282             }
18283             
18284             if(!this.tickable){
18285                 e.preventDefault();
18286             }
18287             
18288         }
18289         return true;
18290     },
18291
18292     /**
18293      * Get the number of selected nodes.
18294      * @return {Number}
18295      */
18296     getSelectionCount : function(){
18297         return this.selections.length;
18298     },
18299
18300     /**
18301      * Get the currently selected nodes.
18302      * @return {Array} An array of HTMLElements
18303      */
18304     getSelectedNodes : function(){
18305         return this.selections;
18306     },
18307
18308     /**
18309      * Get the indexes of the selected nodes.
18310      * @return {Array}
18311      */
18312     getSelectedIndexes : function(){
18313         var indexes = [], s = this.selections;
18314         for(var i = 0, len = s.length; i < len; i++){
18315             indexes.push(s[i].nodeIndex);
18316         }
18317         return indexes;
18318     },
18319
18320     /**
18321      * Clear all selections
18322      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18323      */
18324     clearSelections : function(suppressEvent){
18325         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18326             this.cmp.elements = this.selections;
18327             this.cmp.removeClass(this.selectedClass);
18328             this.selections = [];
18329             if(!suppressEvent){
18330                 this.fireEvent("selectionchange", this, this.selections);
18331             }
18332         }
18333     },
18334
18335     /**
18336      * Returns true if the passed node is selected
18337      * @param {HTMLElement/Number} node The node or node index
18338      * @return {Boolean}
18339      */
18340     isSelected : function(node){
18341         var s = this.selections;
18342         if(s.length < 1){
18343             return false;
18344         }
18345         node = this.getNode(node);
18346         return s.indexOf(node) !== -1;
18347     },
18348
18349     /**
18350      * Selects nodes.
18351      * @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
18352      * @param {Boolean} keepExisting (optional) true to keep existing selections
18353      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18354      */
18355     select : function(nodeInfo, keepExisting, suppressEvent){
18356         if(nodeInfo instanceof Array){
18357             if(!keepExisting){
18358                 this.clearSelections(true);
18359             }
18360             for(var i = 0, len = nodeInfo.length; i < len; i++){
18361                 this.select(nodeInfo[i], true, true);
18362             }
18363             return;
18364         } 
18365         var node = this.getNode(nodeInfo);
18366         if(!node || this.isSelected(node)){
18367             return; // already selected.
18368         }
18369         if(!keepExisting){
18370             this.clearSelections(true);
18371         }
18372         
18373         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18374             Roo.fly(node).addClass(this.selectedClass);
18375             this.selections.push(node);
18376             if(!suppressEvent){
18377                 this.fireEvent("selectionchange", this, this.selections);
18378             }
18379         }
18380         
18381         
18382     },
18383       /**
18384      * Unselects nodes.
18385      * @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
18386      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18387      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18388      */
18389     unselect : function(nodeInfo, keepExisting, suppressEvent)
18390     {
18391         if(nodeInfo instanceof Array){
18392             Roo.each(this.selections, function(s) {
18393                 this.unselect(s, nodeInfo);
18394             }, this);
18395             return;
18396         }
18397         var node = this.getNode(nodeInfo);
18398         if(!node || !this.isSelected(node)){
18399             //Roo.log("not selected");
18400             return; // not selected.
18401         }
18402         // fireevent???
18403         var ns = [];
18404         Roo.each(this.selections, function(s) {
18405             if (s == node ) {
18406                 Roo.fly(node).removeClass(this.selectedClass);
18407
18408                 return;
18409             }
18410             ns.push(s);
18411         },this);
18412         
18413         this.selections= ns;
18414         this.fireEvent("selectionchange", this, this.selections);
18415     },
18416
18417     /**
18418      * Gets a template node.
18419      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18420      * @return {HTMLElement} The node or null if it wasn't found
18421      */
18422     getNode : function(nodeInfo){
18423         if(typeof nodeInfo == "string"){
18424             return document.getElementById(nodeInfo);
18425         }else if(typeof nodeInfo == "number"){
18426             return this.nodes[nodeInfo];
18427         }
18428         return nodeInfo;
18429     },
18430
18431     /**
18432      * Gets a range template nodes.
18433      * @param {Number} startIndex
18434      * @param {Number} endIndex
18435      * @return {Array} An array of nodes
18436      */
18437     getNodes : function(start, end){
18438         var ns = this.nodes;
18439         start = start || 0;
18440         end = typeof end == "undefined" ? ns.length - 1 : end;
18441         var nodes = [];
18442         if(start <= end){
18443             for(var i = start; i <= end; i++){
18444                 nodes.push(ns[i]);
18445             }
18446         } else{
18447             for(var i = start; i >= end; i--){
18448                 nodes.push(ns[i]);
18449             }
18450         }
18451         return nodes;
18452     },
18453
18454     /**
18455      * Finds the index of the passed node
18456      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18457      * @return {Number} The index of the node or -1
18458      */
18459     indexOf : function(node){
18460         node = this.getNode(node);
18461         if(typeof node.nodeIndex == "number"){
18462             return node.nodeIndex;
18463         }
18464         var ns = this.nodes;
18465         for(var i = 0, len = ns.length; i < len; i++){
18466             if(ns[i] == node){
18467                 return i;
18468             }
18469         }
18470         return -1;
18471     }
18472 });
18473 /*
18474  * - LGPL
18475  *
18476  * based on jquery fullcalendar
18477  * 
18478  */
18479
18480 Roo.bootstrap = Roo.bootstrap || {};
18481 /**
18482  * @class Roo.bootstrap.Calendar
18483  * @extends Roo.bootstrap.Component
18484  * Bootstrap Calendar class
18485  * @cfg {Boolean} loadMask (true|false) default false
18486  * @cfg {Object} header generate the user specific header of the calendar, default false
18487
18488  * @constructor
18489  * Create a new Container
18490  * @param {Object} config The config object
18491  */
18492
18493
18494
18495 Roo.bootstrap.Calendar = function(config){
18496     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18497      this.addEvents({
18498         /**
18499              * @event select
18500              * Fires when a date is selected
18501              * @param {DatePicker} this
18502              * @param {Date} date The selected date
18503              */
18504         'select': true,
18505         /**
18506              * @event monthchange
18507              * Fires when the displayed month changes 
18508              * @param {DatePicker} this
18509              * @param {Date} date The selected month
18510              */
18511         'monthchange': true,
18512         /**
18513              * @event evententer
18514              * Fires when mouse over an event
18515              * @param {Calendar} this
18516              * @param {event} Event
18517              */
18518         'evententer': true,
18519         /**
18520              * @event eventleave
18521              * Fires when the mouse leaves an
18522              * @param {Calendar} this
18523              * @param {event}
18524              */
18525         'eventleave': true,
18526         /**
18527              * @event eventclick
18528              * Fires when the mouse click an
18529              * @param {Calendar} this
18530              * @param {event}
18531              */
18532         'eventclick': true
18533         
18534     });
18535
18536 };
18537
18538 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18539     
18540      /**
18541      * @cfg {Number} startDay
18542      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18543      */
18544     startDay : 0,
18545     
18546     loadMask : false,
18547     
18548     header : false,
18549       
18550     getAutoCreate : function(){
18551         
18552         
18553         var fc_button = function(name, corner, style, content ) {
18554             return Roo.apply({},{
18555                 tag : 'span',
18556                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18557                          (corner.length ?
18558                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18559                             ''
18560                         ),
18561                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18562                 unselectable: 'on'
18563             });
18564         };
18565         
18566         var header = {};
18567         
18568         if(!this.header){
18569             header = {
18570                 tag : 'table',
18571                 cls : 'fc-header',
18572                 style : 'width:100%',
18573                 cn : [
18574                     {
18575                         tag: 'tr',
18576                         cn : [
18577                             {
18578                                 tag : 'td',
18579                                 cls : 'fc-header-left',
18580                                 cn : [
18581                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18582                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18583                                     { tag: 'span', cls: 'fc-header-space' },
18584                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18585
18586
18587                                 ]
18588                             },
18589
18590                             {
18591                                 tag : 'td',
18592                                 cls : 'fc-header-center',
18593                                 cn : [
18594                                     {
18595                                         tag: 'span',
18596                                         cls: 'fc-header-title',
18597                                         cn : {
18598                                             tag: 'H2',
18599                                             html : 'month / year'
18600                                         }
18601                                     }
18602
18603                                 ]
18604                             },
18605                             {
18606                                 tag : 'td',
18607                                 cls : 'fc-header-right',
18608                                 cn : [
18609                               /*      fc_button('month', 'left', '', 'month' ),
18610                                     fc_button('week', '', '', 'week' ),
18611                                     fc_button('day', 'right', '', 'day' )
18612                                 */    
18613
18614                                 ]
18615                             }
18616
18617                         ]
18618                     }
18619                 ]
18620             };
18621         }
18622         
18623         header = this.header;
18624         
18625        
18626         var cal_heads = function() {
18627             var ret = [];
18628             // fixme - handle this.
18629             
18630             for (var i =0; i < Date.dayNames.length; i++) {
18631                 var d = Date.dayNames[i];
18632                 ret.push({
18633                     tag: 'th',
18634                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18635                     html : d.substring(0,3)
18636                 });
18637                 
18638             }
18639             ret[0].cls += ' fc-first';
18640             ret[6].cls += ' fc-last';
18641             return ret;
18642         };
18643         var cal_cell = function(n) {
18644             return  {
18645                 tag: 'td',
18646                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18647                 cn : [
18648                     {
18649                         cn : [
18650                             {
18651                                 cls: 'fc-day-number',
18652                                 html: 'D'
18653                             },
18654                             {
18655                                 cls: 'fc-day-content',
18656                              
18657                                 cn : [
18658                                      {
18659                                         style: 'position: relative;' // height: 17px;
18660                                     }
18661                                 ]
18662                             }
18663                             
18664                             
18665                         ]
18666                     }
18667                 ]
18668                 
18669             }
18670         };
18671         var cal_rows = function() {
18672             
18673             var ret = [];
18674             for (var r = 0; r < 6; r++) {
18675                 var row= {
18676                     tag : 'tr',
18677                     cls : 'fc-week',
18678                     cn : []
18679                 };
18680                 
18681                 for (var i =0; i < Date.dayNames.length; i++) {
18682                     var d = Date.dayNames[i];
18683                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18684
18685                 }
18686                 row.cn[0].cls+=' fc-first';
18687                 row.cn[0].cn[0].style = 'min-height:90px';
18688                 row.cn[6].cls+=' fc-last';
18689                 ret.push(row);
18690                 
18691             }
18692             ret[0].cls += ' fc-first';
18693             ret[4].cls += ' fc-prev-last';
18694             ret[5].cls += ' fc-last';
18695             return ret;
18696             
18697         };
18698         
18699         var cal_table = {
18700             tag: 'table',
18701             cls: 'fc-border-separate',
18702             style : 'width:100%',
18703             cellspacing  : 0,
18704             cn : [
18705                 { 
18706                     tag: 'thead',
18707                     cn : [
18708                         { 
18709                             tag: 'tr',
18710                             cls : 'fc-first fc-last',
18711                             cn : cal_heads()
18712                         }
18713                     ]
18714                 },
18715                 { 
18716                     tag: 'tbody',
18717                     cn : cal_rows()
18718                 }
18719                   
18720             ]
18721         };
18722          
18723          var cfg = {
18724             cls : 'fc fc-ltr',
18725             cn : [
18726                 header,
18727                 {
18728                     cls : 'fc-content',
18729                     style : "position: relative;",
18730                     cn : [
18731                         {
18732                             cls : 'fc-view fc-view-month fc-grid',
18733                             style : 'position: relative',
18734                             unselectable : 'on',
18735                             cn : [
18736                                 {
18737                                     cls : 'fc-event-container',
18738                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18739                                 },
18740                                 cal_table
18741                             ]
18742                         }
18743                     ]
18744     
18745                 }
18746            ] 
18747             
18748         };
18749         
18750          
18751         
18752         return cfg;
18753     },
18754     
18755     
18756     initEvents : function()
18757     {
18758         if(!this.store){
18759             throw "can not find store for calendar";
18760         }
18761         
18762         var mark = {
18763             tag: "div",
18764             cls:"x-dlg-mask",
18765             style: "text-align:center",
18766             cn: [
18767                 {
18768                     tag: "div",
18769                     style: "background-color:white;width:50%;margin:250 auto",
18770                     cn: [
18771                         {
18772                             tag: "img",
18773                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18774                         },
18775                         {
18776                             tag: "span",
18777                             html: "Loading"
18778                         }
18779                         
18780                     ]
18781                 }
18782             ]
18783         };
18784         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18785         
18786         var size = this.el.select('.fc-content', true).first().getSize();
18787         this.maskEl.setSize(size.width, size.height);
18788         this.maskEl.enableDisplayMode("block");
18789         if(!this.loadMask){
18790             this.maskEl.hide();
18791         }
18792         
18793         this.store = Roo.factory(this.store, Roo.data);
18794         this.store.on('load', this.onLoad, this);
18795         this.store.on('beforeload', this.onBeforeLoad, this);
18796         
18797         this.resize();
18798         
18799         this.cells = this.el.select('.fc-day',true);
18800         //Roo.log(this.cells);
18801         this.textNodes = this.el.query('.fc-day-number');
18802         this.cells.addClassOnOver('fc-state-hover');
18803         
18804         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18805         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18806         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18807         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18808         
18809         this.on('monthchange', this.onMonthChange, this);
18810         
18811         this.update(new Date().clearTime());
18812     },
18813     
18814     resize : function() {
18815         var sz  = this.el.getSize();
18816         
18817         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18818         this.el.select('.fc-day-content div',true).setHeight(34);
18819     },
18820     
18821     
18822     // private
18823     showPrevMonth : function(e){
18824         this.update(this.activeDate.add("mo", -1));
18825     },
18826     showToday : function(e){
18827         this.update(new Date().clearTime());
18828     },
18829     // private
18830     showNextMonth : function(e){
18831         this.update(this.activeDate.add("mo", 1));
18832     },
18833
18834     // private
18835     showPrevYear : function(){
18836         this.update(this.activeDate.add("y", -1));
18837     },
18838
18839     // private
18840     showNextYear : function(){
18841         this.update(this.activeDate.add("y", 1));
18842     },
18843
18844     
18845    // private
18846     update : function(date)
18847     {
18848         var vd = this.activeDate;
18849         this.activeDate = date;
18850 //        if(vd && this.el){
18851 //            var t = date.getTime();
18852 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18853 //                Roo.log('using add remove');
18854 //                
18855 //                this.fireEvent('monthchange', this, date);
18856 //                
18857 //                this.cells.removeClass("fc-state-highlight");
18858 //                this.cells.each(function(c){
18859 //                   if(c.dateValue == t){
18860 //                       c.addClass("fc-state-highlight");
18861 //                       setTimeout(function(){
18862 //                            try{c.dom.firstChild.focus();}catch(e){}
18863 //                       }, 50);
18864 //                       return false;
18865 //                   }
18866 //                   return true;
18867 //                });
18868 //                return;
18869 //            }
18870 //        }
18871         
18872         var days = date.getDaysInMonth();
18873         
18874         var firstOfMonth = date.getFirstDateOfMonth();
18875         var startingPos = firstOfMonth.getDay()-this.startDay;
18876         
18877         if(startingPos < this.startDay){
18878             startingPos += 7;
18879         }
18880         
18881         var pm = date.add(Date.MONTH, -1);
18882         var prevStart = pm.getDaysInMonth()-startingPos;
18883 //        
18884         this.cells = this.el.select('.fc-day',true);
18885         this.textNodes = this.el.query('.fc-day-number');
18886         this.cells.addClassOnOver('fc-state-hover');
18887         
18888         var cells = this.cells.elements;
18889         var textEls = this.textNodes;
18890         
18891         Roo.each(cells, function(cell){
18892             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18893         });
18894         
18895         days += startingPos;
18896
18897         // convert everything to numbers so it's fast
18898         var day = 86400000;
18899         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18900         //Roo.log(d);
18901         //Roo.log(pm);
18902         //Roo.log(prevStart);
18903         
18904         var today = new Date().clearTime().getTime();
18905         var sel = date.clearTime().getTime();
18906         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18907         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18908         var ddMatch = this.disabledDatesRE;
18909         var ddText = this.disabledDatesText;
18910         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18911         var ddaysText = this.disabledDaysText;
18912         var format = this.format;
18913         
18914         var setCellClass = function(cal, cell){
18915             cell.row = 0;
18916             cell.events = [];
18917             cell.more = [];
18918             //Roo.log('set Cell Class');
18919             cell.title = "";
18920             var t = d.getTime();
18921             
18922             //Roo.log(d);
18923             
18924             cell.dateValue = t;
18925             if(t == today){
18926                 cell.className += " fc-today";
18927                 cell.className += " fc-state-highlight";
18928                 cell.title = cal.todayText;
18929             }
18930             if(t == sel){
18931                 // disable highlight in other month..
18932                 //cell.className += " fc-state-highlight";
18933                 
18934             }
18935             // disabling
18936             if(t < min) {
18937                 cell.className = " fc-state-disabled";
18938                 cell.title = cal.minText;
18939                 return;
18940             }
18941             if(t > max) {
18942                 cell.className = " fc-state-disabled";
18943                 cell.title = cal.maxText;
18944                 return;
18945             }
18946             if(ddays){
18947                 if(ddays.indexOf(d.getDay()) != -1){
18948                     cell.title = ddaysText;
18949                     cell.className = " fc-state-disabled";
18950                 }
18951             }
18952             if(ddMatch && format){
18953                 var fvalue = d.dateFormat(format);
18954                 if(ddMatch.test(fvalue)){
18955                     cell.title = ddText.replace("%0", fvalue);
18956                     cell.className = " fc-state-disabled";
18957                 }
18958             }
18959             
18960             if (!cell.initialClassName) {
18961                 cell.initialClassName = cell.dom.className;
18962             }
18963             
18964             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18965         };
18966
18967         var i = 0;
18968         
18969         for(; i < startingPos; i++) {
18970             textEls[i].innerHTML = (++prevStart);
18971             d.setDate(d.getDate()+1);
18972             
18973             cells[i].className = "fc-past fc-other-month";
18974             setCellClass(this, cells[i]);
18975         }
18976         
18977         var intDay = 0;
18978         
18979         for(; i < days; i++){
18980             intDay = i - startingPos + 1;
18981             textEls[i].innerHTML = (intDay);
18982             d.setDate(d.getDate()+1);
18983             
18984             cells[i].className = ''; // "x-date-active";
18985             setCellClass(this, cells[i]);
18986         }
18987         var extraDays = 0;
18988         
18989         for(; i < 42; i++) {
18990             textEls[i].innerHTML = (++extraDays);
18991             d.setDate(d.getDate()+1);
18992             
18993             cells[i].className = "fc-future fc-other-month";
18994             setCellClass(this, cells[i]);
18995         }
18996         
18997         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
18998         
18999         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19000         
19001         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19002         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19003         
19004         if(totalRows != 6){
19005             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19006             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19007         }
19008         
19009         this.fireEvent('monthchange', this, date);
19010         
19011         
19012         /*
19013         if(!this.internalRender){
19014             var main = this.el.dom.firstChild;
19015             var w = main.offsetWidth;
19016             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19017             Roo.fly(main).setWidth(w);
19018             this.internalRender = true;
19019             // opera does not respect the auto grow header center column
19020             // then, after it gets a width opera refuses to recalculate
19021             // without a second pass
19022             if(Roo.isOpera && !this.secondPass){
19023                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19024                 this.secondPass = true;
19025                 this.update.defer(10, this, [date]);
19026             }
19027         }
19028         */
19029         
19030     },
19031     
19032     findCell : function(dt) {
19033         dt = dt.clearTime().getTime();
19034         var ret = false;
19035         this.cells.each(function(c){
19036             //Roo.log("check " +c.dateValue + '?=' + dt);
19037             if(c.dateValue == dt){
19038                 ret = c;
19039                 return false;
19040             }
19041             return true;
19042         });
19043         
19044         return ret;
19045     },
19046     
19047     findCells : function(ev) {
19048         var s = ev.start.clone().clearTime().getTime();
19049        // Roo.log(s);
19050         var e= ev.end.clone().clearTime().getTime();
19051        // Roo.log(e);
19052         var ret = [];
19053         this.cells.each(function(c){
19054              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19055             
19056             if(c.dateValue > e){
19057                 return ;
19058             }
19059             if(c.dateValue < s){
19060                 return ;
19061             }
19062             ret.push(c);
19063         });
19064         
19065         return ret;    
19066     },
19067     
19068 //    findBestRow: function(cells)
19069 //    {
19070 //        var ret = 0;
19071 //        
19072 //        for (var i =0 ; i < cells.length;i++) {
19073 //            ret  = Math.max(cells[i].rows || 0,ret);
19074 //        }
19075 //        return ret;
19076 //        
19077 //    },
19078     
19079     
19080     addItem : function(ev)
19081     {
19082         // look for vertical location slot in
19083         var cells = this.findCells(ev);
19084         
19085 //        ev.row = this.findBestRow(cells);
19086         
19087         // work out the location.
19088         
19089         var crow = false;
19090         var rows = [];
19091         for(var i =0; i < cells.length; i++) {
19092             
19093             cells[i].row = cells[0].row;
19094             
19095             if(i == 0){
19096                 cells[i].row = cells[i].row + 1;
19097             }
19098             
19099             if (!crow) {
19100                 crow = {
19101                     start : cells[i],
19102                     end :  cells[i]
19103                 };
19104                 continue;
19105             }
19106             if (crow.start.getY() == cells[i].getY()) {
19107                 // on same row.
19108                 crow.end = cells[i];
19109                 continue;
19110             }
19111             // different row.
19112             rows.push(crow);
19113             crow = {
19114                 start: cells[i],
19115                 end : cells[i]
19116             };
19117             
19118         }
19119         
19120         rows.push(crow);
19121         ev.els = [];
19122         ev.rows = rows;
19123         ev.cells = cells;
19124         
19125         cells[0].events.push(ev);
19126         
19127         this.calevents.push(ev);
19128     },
19129     
19130     clearEvents: function() {
19131         
19132         if(!this.calevents){
19133             return;
19134         }
19135         
19136         Roo.each(this.cells.elements, function(c){
19137             c.row = 0;
19138             c.events = [];
19139             c.more = [];
19140         });
19141         
19142         Roo.each(this.calevents, function(e) {
19143             Roo.each(e.els, function(el) {
19144                 el.un('mouseenter' ,this.onEventEnter, this);
19145                 el.un('mouseleave' ,this.onEventLeave, this);
19146                 el.remove();
19147             },this);
19148         },this);
19149         
19150         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19151             e.remove();
19152         });
19153         
19154     },
19155     
19156     renderEvents: function()
19157     {   
19158         var _this = this;
19159         
19160         this.cells.each(function(c) {
19161             
19162             if(c.row < 5){
19163                 return;
19164             }
19165             
19166             var ev = c.events;
19167             
19168             var r = 4;
19169             if(c.row != c.events.length){
19170                 r = 4 - (4 - (c.row - c.events.length));
19171             }
19172             
19173             c.events = ev.slice(0, r);
19174             c.more = ev.slice(r);
19175             
19176             if(c.more.length && c.more.length == 1){
19177                 c.events.push(c.more.pop());
19178             }
19179             
19180             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19181             
19182         });
19183             
19184         this.cells.each(function(c) {
19185             
19186             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19187             
19188             
19189             for (var e = 0; e < c.events.length; e++){
19190                 var ev = c.events[e];
19191                 var rows = ev.rows;
19192                 
19193                 for(var i = 0; i < rows.length; i++) {
19194                 
19195                     // how many rows should it span..
19196
19197                     var  cfg = {
19198                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19199                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19200
19201                         unselectable : "on",
19202                         cn : [
19203                             {
19204                                 cls: 'fc-event-inner',
19205                                 cn : [
19206     //                                {
19207     //                                  tag:'span',
19208     //                                  cls: 'fc-event-time',
19209     //                                  html : cells.length > 1 ? '' : ev.time
19210     //                                },
19211                                     {
19212                                       tag:'span',
19213                                       cls: 'fc-event-title',
19214                                       html : String.format('{0}', ev.title)
19215                                     }
19216
19217
19218                                 ]
19219                             },
19220                             {
19221                                 cls: 'ui-resizable-handle ui-resizable-e',
19222                                 html : '&nbsp;&nbsp;&nbsp'
19223                             }
19224
19225                         ]
19226                     };
19227
19228                     if (i == 0) {
19229                         cfg.cls += ' fc-event-start';
19230                     }
19231                     if ((i+1) == rows.length) {
19232                         cfg.cls += ' fc-event-end';
19233                     }
19234
19235                     var ctr = _this.el.select('.fc-event-container',true).first();
19236                     var cg = ctr.createChild(cfg);
19237
19238                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19239                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19240
19241                     var r = (c.more.length) ? 1 : 0;
19242                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19243                     cg.setWidth(ebox.right - sbox.x -2);
19244
19245                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19246                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19247                     cg.on('click', _this.onEventClick, _this, ev);
19248
19249                     ev.els.push(cg);
19250                     
19251                 }
19252                 
19253             }
19254             
19255             
19256             if(c.more.length){
19257                 var  cfg = {
19258                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19259                     style : 'position: absolute',
19260                     unselectable : "on",
19261                     cn : [
19262                         {
19263                             cls: 'fc-event-inner',
19264                             cn : [
19265                                 {
19266                                   tag:'span',
19267                                   cls: 'fc-event-title',
19268                                   html : 'More'
19269                                 }
19270
19271
19272                             ]
19273                         },
19274                         {
19275                             cls: 'ui-resizable-handle ui-resizable-e',
19276                             html : '&nbsp;&nbsp;&nbsp'
19277                         }
19278
19279                     ]
19280                 };
19281
19282                 var ctr = _this.el.select('.fc-event-container',true).first();
19283                 var cg = ctr.createChild(cfg);
19284
19285                 var sbox = c.select('.fc-day-content',true).first().getBox();
19286                 var ebox = c.select('.fc-day-content',true).first().getBox();
19287                 //Roo.log(cg);
19288                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19289                 cg.setWidth(ebox.right - sbox.x -2);
19290
19291                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19292                 
19293             }
19294             
19295         });
19296         
19297         
19298         
19299     },
19300     
19301     onEventEnter: function (e, el,event,d) {
19302         this.fireEvent('evententer', this, el, event);
19303     },
19304     
19305     onEventLeave: function (e, el,event,d) {
19306         this.fireEvent('eventleave', this, el, event);
19307     },
19308     
19309     onEventClick: function (e, el,event,d) {
19310         this.fireEvent('eventclick', this, el, event);
19311     },
19312     
19313     onMonthChange: function () {
19314         this.store.load();
19315     },
19316     
19317     onMoreEventClick: function(e, el, more)
19318     {
19319         var _this = this;
19320         
19321         this.calpopover.placement = 'right';
19322         this.calpopover.setTitle('More');
19323         
19324         this.calpopover.setContent('');
19325         
19326         var ctr = this.calpopover.el.select('.popover-content', true).first();
19327         
19328         Roo.each(more, function(m){
19329             var cfg = {
19330                 cls : 'fc-event-hori fc-event-draggable',
19331                 html : m.title
19332             };
19333             var cg = ctr.createChild(cfg);
19334             
19335             cg.on('click', _this.onEventClick, _this, m);
19336         });
19337         
19338         this.calpopover.show(el);
19339         
19340         
19341     },
19342     
19343     onLoad: function () 
19344     {   
19345         this.calevents = [];
19346         var cal = this;
19347         
19348         if(this.store.getCount() > 0){
19349             this.store.data.each(function(d){
19350                cal.addItem({
19351                     id : d.data.id,
19352                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19353                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19354                     time : d.data.start_time,
19355                     title : d.data.title,
19356                     description : d.data.description,
19357                     venue : d.data.venue
19358                 });
19359             });
19360         }
19361         
19362         this.renderEvents();
19363         
19364         if(this.calevents.length && this.loadMask){
19365             this.maskEl.hide();
19366         }
19367     },
19368     
19369     onBeforeLoad: function()
19370     {
19371         this.clearEvents();
19372         if(this.loadMask){
19373             this.maskEl.show();
19374         }
19375     }
19376 });
19377
19378  
19379  /*
19380  * - LGPL
19381  *
19382  * element
19383  * 
19384  */
19385
19386 /**
19387  * @class Roo.bootstrap.Popover
19388  * @extends Roo.bootstrap.Component
19389  * Bootstrap Popover class
19390  * @cfg {String} html contents of the popover   (or false to use children..)
19391  * @cfg {String} title of popover (or false to hide)
19392  * @cfg {String} placement how it is placed
19393  * @cfg {String} trigger click || hover (or false to trigger manually)
19394  * @cfg {String} over what (parent or false to trigger manually.)
19395  * @cfg {Number} delay - delay before showing
19396  
19397  * @constructor
19398  * Create a new Popover
19399  * @param {Object} config The config object
19400  */
19401
19402 Roo.bootstrap.Popover = function(config){
19403     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19404     
19405     this.addEvents({
19406         // raw events
19407          /**
19408          * @event show
19409          * After the popover show
19410          * 
19411          * @param {Roo.bootstrap.Popover} this
19412          */
19413         "show" : true,
19414         /**
19415          * @event hide
19416          * After the popover hide
19417          * 
19418          * @param {Roo.bootstrap.Popover} this
19419          */
19420         "hide" : true
19421     });
19422 };
19423
19424 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19425     
19426     title: 'Fill in a title',
19427     html: false,
19428     
19429     placement : 'right',
19430     trigger : 'hover', // hover
19431     
19432     delay : 0,
19433     
19434     over: 'parent',
19435     
19436     can_build_overlaid : false,
19437     
19438     getChildContainer : function()
19439     {
19440         return this.el.select('.popover-content',true).first();
19441     },
19442     
19443     getAutoCreate : function(){
19444          
19445         var cfg = {
19446            cls : 'popover roo-dynamic',
19447            style: 'display:block',
19448            cn : [
19449                 {
19450                     cls : 'arrow'
19451                 },
19452                 {
19453                     cls : 'popover-inner',
19454                     cn : [
19455                         {
19456                             tag: 'h3',
19457                             cls: 'popover-title popover-header',
19458                             html : this.title
19459                         },
19460                         {
19461                             cls : 'popover-content popover-body',
19462                             html : this.html
19463                         }
19464                     ]
19465                     
19466                 }
19467            ]
19468         };
19469         
19470         return cfg;
19471     },
19472     setTitle: function(str)
19473     {
19474         this.title = str;
19475         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19476     },
19477     setContent: function(str)
19478     {
19479         this.html = str;
19480         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19481     },
19482     // as it get's added to the bottom of the page.
19483     onRender : function(ct, position)
19484     {
19485         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19486         if(!this.el){
19487             var cfg = Roo.apply({},  this.getAutoCreate());
19488             cfg.id = Roo.id();
19489             
19490             if (this.cls) {
19491                 cfg.cls += ' ' + this.cls;
19492             }
19493             if (this.style) {
19494                 cfg.style = this.style;
19495             }
19496             //Roo.log("adding to ");
19497             this.el = Roo.get(document.body).createChild(cfg, position);
19498 //            Roo.log(this.el);
19499         }
19500         this.initEvents();
19501     },
19502     
19503     initEvents : function()
19504     {
19505         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19506         this.el.enableDisplayMode('block');
19507         this.el.hide();
19508         if (this.over === false) {
19509             return; 
19510         }
19511         if (this.triggers === false) {
19512             return;
19513         }
19514         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19515         var triggers = this.trigger ? this.trigger.split(' ') : [];
19516         Roo.each(triggers, function(trigger) {
19517         
19518             if (trigger == 'click') {
19519                 on_el.on('click', this.toggle, this);
19520             } else if (trigger != 'manual') {
19521                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19522                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19523       
19524                 on_el.on(eventIn  ,this.enter, this);
19525                 on_el.on(eventOut, this.leave, this);
19526             }
19527         }, this);
19528         
19529     },
19530     
19531     
19532     // private
19533     timeout : null,
19534     hoverState : null,
19535     
19536     toggle : function () {
19537         this.hoverState == 'in' ? this.leave() : this.enter();
19538     },
19539     
19540     enter : function () {
19541         
19542         clearTimeout(this.timeout);
19543     
19544         this.hoverState = 'in';
19545     
19546         if (!this.delay || !this.delay.show) {
19547             this.show();
19548             return;
19549         }
19550         var _t = this;
19551         this.timeout = setTimeout(function () {
19552             if (_t.hoverState == 'in') {
19553                 _t.show();
19554             }
19555         }, this.delay.show)
19556     },
19557     
19558     leave : function() {
19559         clearTimeout(this.timeout);
19560     
19561         this.hoverState = 'out';
19562     
19563         if (!this.delay || !this.delay.hide) {
19564             this.hide();
19565             return;
19566         }
19567         var _t = this;
19568         this.timeout = setTimeout(function () {
19569             if (_t.hoverState == 'out') {
19570                 _t.hide();
19571             }
19572         }, this.delay.hide)
19573     },
19574     
19575     show : function (on_el)
19576     {
19577         if (!on_el) {
19578             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19579         }
19580         
19581         // set content.
19582         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19583         if (this.html !== false) {
19584             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19585         }
19586         this.el.removeClass([
19587             'fade','top','bottom', 'left', 'right','in',
19588             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19589         ]);
19590         if (!this.title.length) {
19591             this.el.select('.popover-title',true).hide();
19592         }
19593         
19594         var placement = typeof this.placement == 'function' ?
19595             this.placement.call(this, this.el, on_el) :
19596             this.placement;
19597             
19598         var autoToken = /\s?auto?\s?/i;
19599         var autoPlace = autoToken.test(placement);
19600         if (autoPlace) {
19601             placement = placement.replace(autoToken, '') || 'top';
19602         }
19603         
19604         //this.el.detach()
19605         //this.el.setXY([0,0]);
19606         this.el.show();
19607         this.el.dom.style.display='block';
19608         this.el.addClass(placement);
19609         
19610         //this.el.appendTo(on_el);
19611         
19612         var p = this.getPosition();
19613         var box = this.el.getBox();
19614         
19615         if (autoPlace) {
19616             // fixme..
19617         }
19618         var align = Roo.bootstrap.Popover.alignment[placement];
19619         
19620 //        Roo.log(align);
19621         this.el.alignTo(on_el, align[0],align[1]);
19622         //var arrow = this.el.select('.arrow',true).first();
19623         //arrow.set(align[2], 
19624         
19625         this.el.addClass('in');
19626         
19627         
19628         if (this.el.hasClass('fade')) {
19629             // fade it?
19630         }
19631         
19632         this.hoverState = 'in';
19633         
19634         this.fireEvent('show', this);
19635         
19636     },
19637     hide : function()
19638     {
19639         this.el.setXY([0,0]);
19640         this.el.removeClass('in');
19641         this.el.hide();
19642         this.hoverState = null;
19643         
19644         this.fireEvent('hide', this);
19645     }
19646     
19647 });
19648
19649 Roo.bootstrap.Popover.alignment = {
19650     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19651     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19652     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19653     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19654 };
19655
19656  /*
19657  * - LGPL
19658  *
19659  * Progress
19660  * 
19661  */
19662
19663 /**
19664  * @class Roo.bootstrap.Progress
19665  * @extends Roo.bootstrap.Component
19666  * Bootstrap Progress class
19667  * @cfg {Boolean} striped striped of the progress bar
19668  * @cfg {Boolean} active animated of the progress bar
19669  * 
19670  * 
19671  * @constructor
19672  * Create a new Progress
19673  * @param {Object} config The config object
19674  */
19675
19676 Roo.bootstrap.Progress = function(config){
19677     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19678 };
19679
19680 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19681     
19682     striped : false,
19683     active: false,
19684     
19685     getAutoCreate : function(){
19686         var cfg = {
19687             tag: 'div',
19688             cls: 'progress'
19689         };
19690         
19691         
19692         if(this.striped){
19693             cfg.cls += ' progress-striped';
19694         }
19695       
19696         if(this.active){
19697             cfg.cls += ' active';
19698         }
19699         
19700         
19701         return cfg;
19702     }
19703    
19704 });
19705
19706  
19707
19708  /*
19709  * - LGPL
19710  *
19711  * ProgressBar
19712  * 
19713  */
19714
19715 /**
19716  * @class Roo.bootstrap.ProgressBar
19717  * @extends Roo.bootstrap.Component
19718  * Bootstrap ProgressBar class
19719  * @cfg {Number} aria_valuenow aria-value now
19720  * @cfg {Number} aria_valuemin aria-value min
19721  * @cfg {Number} aria_valuemax aria-value max
19722  * @cfg {String} label label for the progress bar
19723  * @cfg {String} panel (success | info | warning | danger )
19724  * @cfg {String} role role of the progress bar
19725  * @cfg {String} sr_only text
19726  * 
19727  * 
19728  * @constructor
19729  * Create a new ProgressBar
19730  * @param {Object} config The config object
19731  */
19732
19733 Roo.bootstrap.ProgressBar = function(config){
19734     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19735 };
19736
19737 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19738     
19739     aria_valuenow : 0,
19740     aria_valuemin : 0,
19741     aria_valuemax : 100,
19742     label : false,
19743     panel : false,
19744     role : false,
19745     sr_only: false,
19746     
19747     getAutoCreate : function()
19748     {
19749         
19750         var cfg = {
19751             tag: 'div',
19752             cls: 'progress-bar',
19753             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19754         };
19755         
19756         if(this.sr_only){
19757             cfg.cn = {
19758                 tag: 'span',
19759                 cls: 'sr-only',
19760                 html: this.sr_only
19761             }
19762         }
19763         
19764         if(this.role){
19765             cfg.role = this.role;
19766         }
19767         
19768         if(this.aria_valuenow){
19769             cfg['aria-valuenow'] = this.aria_valuenow;
19770         }
19771         
19772         if(this.aria_valuemin){
19773             cfg['aria-valuemin'] = this.aria_valuemin;
19774         }
19775         
19776         if(this.aria_valuemax){
19777             cfg['aria-valuemax'] = this.aria_valuemax;
19778         }
19779         
19780         if(this.label && !this.sr_only){
19781             cfg.html = this.label;
19782         }
19783         
19784         if(this.panel){
19785             cfg.cls += ' progress-bar-' + this.panel;
19786         }
19787         
19788         return cfg;
19789     },
19790     
19791     update : function(aria_valuenow)
19792     {
19793         this.aria_valuenow = aria_valuenow;
19794         
19795         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19796     }
19797    
19798 });
19799
19800  
19801
19802  /*
19803  * - LGPL
19804  *
19805  * column
19806  * 
19807  */
19808
19809 /**
19810  * @class Roo.bootstrap.TabGroup
19811  * @extends Roo.bootstrap.Column
19812  * Bootstrap Column class
19813  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19814  * @cfg {Boolean} carousel true to make the group behave like a carousel
19815  * @cfg {Boolean} bullets show bullets for the panels
19816  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19817  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19818  * @cfg {Boolean} showarrow (true|false) show arrow default true
19819  * 
19820  * @constructor
19821  * Create a new TabGroup
19822  * @param {Object} config The config object
19823  */
19824
19825 Roo.bootstrap.TabGroup = function(config){
19826     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19827     if (!this.navId) {
19828         this.navId = Roo.id();
19829     }
19830     this.tabs = [];
19831     Roo.bootstrap.TabGroup.register(this);
19832     
19833 };
19834
19835 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19836     
19837     carousel : false,
19838     transition : false,
19839     bullets : 0,
19840     timer : 0,
19841     autoslide : false,
19842     slideFn : false,
19843     slideOnTouch : false,
19844     showarrow : true,
19845     
19846     getAutoCreate : function()
19847     {
19848         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19849         
19850         cfg.cls += ' tab-content';
19851         
19852         if (this.carousel) {
19853             cfg.cls += ' carousel slide';
19854             
19855             cfg.cn = [{
19856                cls : 'carousel-inner',
19857                cn : []
19858             }];
19859         
19860             if(this.bullets  && !Roo.isTouch){
19861                 
19862                 var bullets = {
19863                     cls : 'carousel-bullets',
19864                     cn : []
19865                 };
19866                
19867                 if(this.bullets_cls){
19868                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19869                 }
19870                 
19871                 bullets.cn.push({
19872                     cls : 'clear'
19873                 });
19874                 
19875                 cfg.cn[0].cn.push(bullets);
19876             }
19877             
19878             if(this.showarrow){
19879                 cfg.cn[0].cn.push({
19880                     tag : 'div',
19881                     class : 'carousel-arrow',
19882                     cn : [
19883                         {
19884                             tag : 'div',
19885                             class : 'carousel-prev',
19886                             cn : [
19887                                 {
19888                                     tag : 'i',
19889                                     class : 'fa fa-chevron-left'
19890                                 }
19891                             ]
19892                         },
19893                         {
19894                             tag : 'div',
19895                             class : 'carousel-next',
19896                             cn : [
19897                                 {
19898                                     tag : 'i',
19899                                     class : 'fa fa-chevron-right'
19900                                 }
19901                             ]
19902                         }
19903                     ]
19904                 });
19905             }
19906             
19907         }
19908         
19909         return cfg;
19910     },
19911     
19912     initEvents:  function()
19913     {
19914 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19915 //            this.el.on("touchstart", this.onTouchStart, this);
19916 //        }
19917         
19918         if(this.autoslide){
19919             var _this = this;
19920             
19921             this.slideFn = window.setInterval(function() {
19922                 _this.showPanelNext();
19923             }, this.timer);
19924         }
19925         
19926         if(this.showarrow){
19927             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19928             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19929         }
19930         
19931         
19932     },
19933     
19934 //    onTouchStart : function(e, el, o)
19935 //    {
19936 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19937 //            return;
19938 //        }
19939 //        
19940 //        this.showPanelNext();
19941 //    },
19942     
19943     
19944     getChildContainer : function()
19945     {
19946         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19947     },
19948     
19949     /**
19950     * register a Navigation item
19951     * @param {Roo.bootstrap.NavItem} the navitem to add
19952     */
19953     register : function(item)
19954     {
19955         this.tabs.push( item);
19956         item.navId = this.navId; // not really needed..
19957         this.addBullet();
19958     
19959     },
19960     
19961     getActivePanel : function()
19962     {
19963         var r = false;
19964         Roo.each(this.tabs, function(t) {
19965             if (t.active) {
19966                 r = t;
19967                 return false;
19968             }
19969             return null;
19970         });
19971         return r;
19972         
19973     },
19974     getPanelByName : function(n)
19975     {
19976         var r = false;
19977         Roo.each(this.tabs, function(t) {
19978             if (t.tabId == n) {
19979                 r = t;
19980                 return false;
19981             }
19982             return null;
19983         });
19984         return r;
19985     },
19986     indexOfPanel : function(p)
19987     {
19988         var r = false;
19989         Roo.each(this.tabs, function(t,i) {
19990             if (t.tabId == p.tabId) {
19991                 r = i;
19992                 return false;
19993             }
19994             return null;
19995         });
19996         return r;
19997     },
19998     /**
19999      * show a specific panel
20000      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20001      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20002      */
20003     showPanel : function (pan)
20004     {
20005         if(this.transition || typeof(pan) == 'undefined'){
20006             Roo.log("waiting for the transitionend");
20007             return false;
20008         }
20009         
20010         if (typeof(pan) == 'number') {
20011             pan = this.tabs[pan];
20012         }
20013         
20014         if (typeof(pan) == 'string') {
20015             pan = this.getPanelByName(pan);
20016         }
20017         
20018         var cur = this.getActivePanel();
20019         
20020         if(!pan || !cur){
20021             Roo.log('pan or acitve pan is undefined');
20022             return false;
20023         }
20024         
20025         if (pan.tabId == this.getActivePanel().tabId) {
20026             return true;
20027         }
20028         
20029         if (false === cur.fireEvent('beforedeactivate')) {
20030             return false;
20031         }
20032         
20033         if(this.bullets > 0 && !Roo.isTouch){
20034             this.setActiveBullet(this.indexOfPanel(pan));
20035         }
20036         
20037         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20038             
20039             //class="carousel-item carousel-item-next carousel-item-left"
20040             
20041             this.transition = true;
20042             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20043             var lr = dir == 'next' ? 'left' : 'right';
20044             pan.el.addClass(dir); // or prev
20045             pan.el.addClass('carousel-item-' + dir); // or prev
20046             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20047             cur.el.addClass(lr); // or right
20048             pan.el.addClass(lr);
20049             cur.el.addClass('carousel-item-' +lr); // or right
20050             pan.el.addClass('carousel-item-' +lr);
20051             
20052             
20053             var _this = this;
20054             cur.el.on('transitionend', function() {
20055                 Roo.log("trans end?");
20056                 
20057                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20058                 pan.setActive(true);
20059                 
20060                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20061                 cur.setActive(false);
20062                 
20063                 _this.transition = false;
20064                 
20065             }, this, { single:  true } );
20066             
20067             return true;
20068         }
20069         
20070         cur.setActive(false);
20071         pan.setActive(true);
20072         
20073         return true;
20074         
20075     },
20076     showPanelNext : function()
20077     {
20078         var i = this.indexOfPanel(this.getActivePanel());
20079         
20080         if (i >= this.tabs.length - 1 && !this.autoslide) {
20081             return;
20082         }
20083         
20084         if (i >= this.tabs.length - 1 && this.autoslide) {
20085             i = -1;
20086         }
20087         
20088         this.showPanel(this.tabs[i+1]);
20089     },
20090     
20091     showPanelPrev : function()
20092     {
20093         var i = this.indexOfPanel(this.getActivePanel());
20094         
20095         if (i  < 1 && !this.autoslide) {
20096             return;
20097         }
20098         
20099         if (i < 1 && this.autoslide) {
20100             i = this.tabs.length;
20101         }
20102         
20103         this.showPanel(this.tabs[i-1]);
20104     },
20105     
20106     
20107     addBullet: function()
20108     {
20109         if(!this.bullets || Roo.isTouch){
20110             return;
20111         }
20112         var ctr = this.el.select('.carousel-bullets',true).first();
20113         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20114         var bullet = ctr.createChild({
20115             cls : 'bullet bullet-' + i
20116         },ctr.dom.lastChild);
20117         
20118         
20119         var _this = this;
20120         
20121         bullet.on('click', (function(e, el, o, ii, t){
20122
20123             e.preventDefault();
20124
20125             this.showPanel(ii);
20126
20127             if(this.autoslide && this.slideFn){
20128                 clearInterval(this.slideFn);
20129                 this.slideFn = window.setInterval(function() {
20130                     _this.showPanelNext();
20131                 }, this.timer);
20132             }
20133
20134         }).createDelegate(this, [i, bullet], true));
20135                 
20136         
20137     },
20138      
20139     setActiveBullet : function(i)
20140     {
20141         if(Roo.isTouch){
20142             return;
20143         }
20144         
20145         Roo.each(this.el.select('.bullet', true).elements, function(el){
20146             el.removeClass('selected');
20147         });
20148
20149         var bullet = this.el.select('.bullet-' + i, true).first();
20150         
20151         if(!bullet){
20152             return;
20153         }
20154         
20155         bullet.addClass('selected');
20156     }
20157     
20158     
20159   
20160 });
20161
20162  
20163
20164  
20165  
20166 Roo.apply(Roo.bootstrap.TabGroup, {
20167     
20168     groups: {},
20169      /**
20170     * register a Navigation Group
20171     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20172     */
20173     register : function(navgrp)
20174     {
20175         this.groups[navgrp.navId] = navgrp;
20176         
20177     },
20178     /**
20179     * fetch a Navigation Group based on the navigation ID
20180     * if one does not exist , it will get created.
20181     * @param {string} the navgroup to add
20182     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20183     */
20184     get: function(navId) {
20185         if (typeof(this.groups[navId]) == 'undefined') {
20186             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20187         }
20188         return this.groups[navId] ;
20189     }
20190     
20191     
20192     
20193 });
20194
20195  /*
20196  * - LGPL
20197  *
20198  * TabPanel
20199  * 
20200  */
20201
20202 /**
20203  * @class Roo.bootstrap.TabPanel
20204  * @extends Roo.bootstrap.Component
20205  * Bootstrap TabPanel class
20206  * @cfg {Boolean} active panel active
20207  * @cfg {String} html panel content
20208  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20209  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20210  * @cfg {String} href click to link..
20211  * 
20212  * 
20213  * @constructor
20214  * Create a new TabPanel
20215  * @param {Object} config The config object
20216  */
20217
20218 Roo.bootstrap.TabPanel = function(config){
20219     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20220     this.addEvents({
20221         /**
20222              * @event changed
20223              * Fires when the active status changes
20224              * @param {Roo.bootstrap.TabPanel} this
20225              * @param {Boolean} state the new state
20226             
20227          */
20228         'changed': true,
20229         /**
20230              * @event beforedeactivate
20231              * Fires before a tab is de-activated - can be used to do validation on a form.
20232              * @param {Roo.bootstrap.TabPanel} this
20233              * @return {Boolean} false if there is an error
20234             
20235          */
20236         'beforedeactivate': true
20237      });
20238     
20239     this.tabId = this.tabId || Roo.id();
20240   
20241 };
20242
20243 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20244     
20245     active: false,
20246     html: false,
20247     tabId: false,
20248     navId : false,
20249     href : '',
20250     
20251     getAutoCreate : function(){
20252         
20253         
20254         var cfg = {
20255             tag: 'div',
20256             // item is needed for carousel - not sure if it has any effect otherwise
20257             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20258             html: this.html || ''
20259         };
20260         
20261         if(this.active){
20262             cfg.cls += ' active';
20263         }
20264         
20265         if(this.tabId){
20266             cfg.tabId = this.tabId;
20267         }
20268         
20269         
20270         
20271         return cfg;
20272     },
20273     
20274     initEvents:  function()
20275     {
20276         var p = this.parent();
20277         
20278         this.navId = this.navId || p.navId;
20279         
20280         if (typeof(this.navId) != 'undefined') {
20281             // not really needed.. but just in case.. parent should be a NavGroup.
20282             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20283             
20284             tg.register(this);
20285             
20286             var i = tg.tabs.length - 1;
20287             
20288             if(this.active && tg.bullets > 0 && i < tg.bullets){
20289                 tg.setActiveBullet(i);
20290             }
20291         }
20292         
20293         this.el.on('click', this.onClick, this);
20294         
20295         if(Roo.isTouch){
20296             this.el.on("touchstart", this.onTouchStart, this);
20297             this.el.on("touchmove", this.onTouchMove, this);
20298             this.el.on("touchend", this.onTouchEnd, this);
20299         }
20300         
20301     },
20302     
20303     onRender : function(ct, position)
20304     {
20305         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20306     },
20307     
20308     setActive : function(state)
20309     {
20310         Roo.log("panel - set active " + this.tabId + "=" + state);
20311         
20312         this.active = state;
20313         if (!state) {
20314             this.el.removeClass('active');
20315             
20316         } else  if (!this.el.hasClass('active')) {
20317             this.el.addClass('active');
20318         }
20319         
20320         this.fireEvent('changed', this, state);
20321     },
20322     
20323     onClick : function(e)
20324     {
20325         e.preventDefault();
20326         
20327         if(!this.href.length){
20328             return;
20329         }
20330         
20331         window.location.href = this.href;
20332     },
20333     
20334     startX : 0,
20335     startY : 0,
20336     endX : 0,
20337     endY : 0,
20338     swiping : false,
20339     
20340     onTouchStart : function(e)
20341     {
20342         this.swiping = false;
20343         
20344         this.startX = e.browserEvent.touches[0].clientX;
20345         this.startY = e.browserEvent.touches[0].clientY;
20346     },
20347     
20348     onTouchMove : function(e)
20349     {
20350         this.swiping = true;
20351         
20352         this.endX = e.browserEvent.touches[0].clientX;
20353         this.endY = e.browserEvent.touches[0].clientY;
20354     },
20355     
20356     onTouchEnd : function(e)
20357     {
20358         if(!this.swiping){
20359             this.onClick(e);
20360             return;
20361         }
20362         
20363         var tabGroup = this.parent();
20364         
20365         if(this.endX > this.startX){ // swiping right
20366             tabGroup.showPanelPrev();
20367             return;
20368         }
20369         
20370         if(this.startX > this.endX){ // swiping left
20371             tabGroup.showPanelNext();
20372             return;
20373         }
20374     }
20375     
20376     
20377 });
20378  
20379
20380  
20381
20382  /*
20383  * - LGPL
20384  *
20385  * DateField
20386  * 
20387  */
20388
20389 /**
20390  * @class Roo.bootstrap.DateField
20391  * @extends Roo.bootstrap.Input
20392  * Bootstrap DateField class
20393  * @cfg {Number} weekStart default 0
20394  * @cfg {String} viewMode default empty, (months|years)
20395  * @cfg {String} minViewMode default empty, (months|years)
20396  * @cfg {Number} startDate default -Infinity
20397  * @cfg {Number} endDate default Infinity
20398  * @cfg {Boolean} todayHighlight default false
20399  * @cfg {Boolean} todayBtn default false
20400  * @cfg {Boolean} calendarWeeks default false
20401  * @cfg {Object} daysOfWeekDisabled default empty
20402  * @cfg {Boolean} singleMode default false (true | false)
20403  * 
20404  * @cfg {Boolean} keyboardNavigation default true
20405  * @cfg {String} language default en
20406  * 
20407  * @constructor
20408  * Create a new DateField
20409  * @param {Object} config The config object
20410  */
20411
20412 Roo.bootstrap.DateField = function(config){
20413     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20414      this.addEvents({
20415             /**
20416              * @event show
20417              * Fires when this field show.
20418              * @param {Roo.bootstrap.DateField} this
20419              * @param {Mixed} date The date value
20420              */
20421             show : true,
20422             /**
20423              * @event show
20424              * Fires when this field hide.
20425              * @param {Roo.bootstrap.DateField} this
20426              * @param {Mixed} date The date value
20427              */
20428             hide : true,
20429             /**
20430              * @event select
20431              * Fires when select a date.
20432              * @param {Roo.bootstrap.DateField} this
20433              * @param {Mixed} date The date value
20434              */
20435             select : true,
20436             /**
20437              * @event beforeselect
20438              * Fires when before select a date.
20439              * @param {Roo.bootstrap.DateField} this
20440              * @param {Mixed} date The date value
20441              */
20442             beforeselect : true
20443         });
20444 };
20445
20446 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20447     
20448     /**
20449      * @cfg {String} format
20450      * The default date format string which can be overriden for localization support.  The format must be
20451      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20452      */
20453     format : "m/d/y",
20454     /**
20455      * @cfg {String} altFormats
20456      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20457      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20458      */
20459     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20460     
20461     weekStart : 0,
20462     
20463     viewMode : '',
20464     
20465     minViewMode : '',
20466     
20467     todayHighlight : false,
20468     
20469     todayBtn: false,
20470     
20471     language: 'en',
20472     
20473     keyboardNavigation: true,
20474     
20475     calendarWeeks: false,
20476     
20477     startDate: -Infinity,
20478     
20479     endDate: Infinity,
20480     
20481     daysOfWeekDisabled: [],
20482     
20483     _events: [],
20484     
20485     singleMode : false,
20486     
20487     UTCDate: function()
20488     {
20489         return new Date(Date.UTC.apply(Date, arguments));
20490     },
20491     
20492     UTCToday: function()
20493     {
20494         var today = new Date();
20495         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20496     },
20497     
20498     getDate: function() {
20499             var d = this.getUTCDate();
20500             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20501     },
20502     
20503     getUTCDate: function() {
20504             return this.date;
20505     },
20506     
20507     setDate: function(d) {
20508             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20509     },
20510     
20511     setUTCDate: function(d) {
20512             this.date = d;
20513             this.setValue(this.formatDate(this.date));
20514     },
20515         
20516     onRender: function(ct, position)
20517     {
20518         
20519         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20520         
20521         this.language = this.language || 'en';
20522         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20523         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20524         
20525         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20526         this.format = this.format || 'm/d/y';
20527         this.isInline = false;
20528         this.isInput = true;
20529         this.component = this.el.select('.add-on', true).first() || false;
20530         this.component = (this.component && this.component.length === 0) ? false : this.component;
20531         this.hasInput = this.component && this.inputEl().length;
20532         
20533         if (typeof(this.minViewMode === 'string')) {
20534             switch (this.minViewMode) {
20535                 case 'months':
20536                     this.minViewMode = 1;
20537                     break;
20538                 case 'years':
20539                     this.minViewMode = 2;
20540                     break;
20541                 default:
20542                     this.minViewMode = 0;
20543                     break;
20544             }
20545         }
20546         
20547         if (typeof(this.viewMode === 'string')) {
20548             switch (this.viewMode) {
20549                 case 'months':
20550                     this.viewMode = 1;
20551                     break;
20552                 case 'years':
20553                     this.viewMode = 2;
20554                     break;
20555                 default:
20556                     this.viewMode = 0;
20557                     break;
20558             }
20559         }
20560                 
20561         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20562         
20563 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20564         
20565         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20566         
20567         this.picker().on('mousedown', this.onMousedown, this);
20568         this.picker().on('click', this.onClick, this);
20569         
20570         this.picker().addClass('datepicker-dropdown');
20571         
20572         this.startViewMode = this.viewMode;
20573         
20574         if(this.singleMode){
20575             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20576                 v.setVisibilityMode(Roo.Element.DISPLAY);
20577                 v.hide();
20578             });
20579             
20580             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20581                 v.setStyle('width', '189px');
20582             });
20583         }
20584         
20585         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20586             if(!this.calendarWeeks){
20587                 v.remove();
20588                 return;
20589             }
20590             
20591             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20592             v.attr('colspan', function(i, val){
20593                 return parseInt(val) + 1;
20594             });
20595         });
20596                         
20597         
20598         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20599         
20600         this.setStartDate(this.startDate);
20601         this.setEndDate(this.endDate);
20602         
20603         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20604         
20605         this.fillDow();
20606         this.fillMonths();
20607         this.update();
20608         this.showMode();
20609         
20610         if(this.isInline) {
20611             this.showPopup();
20612         }
20613     },
20614     
20615     picker : function()
20616     {
20617         return this.pickerEl;
20618 //        return this.el.select('.datepicker', true).first();
20619     },
20620     
20621     fillDow: function()
20622     {
20623         var dowCnt = this.weekStart;
20624         
20625         var dow = {
20626             tag: 'tr',
20627             cn: [
20628                 
20629             ]
20630         };
20631         
20632         if(this.calendarWeeks){
20633             dow.cn.push({
20634                 tag: 'th',
20635                 cls: 'cw',
20636                 html: '&nbsp;'
20637             })
20638         }
20639         
20640         while (dowCnt < this.weekStart + 7) {
20641             dow.cn.push({
20642                 tag: 'th',
20643                 cls: 'dow',
20644                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20645             });
20646         }
20647         
20648         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20649     },
20650     
20651     fillMonths: function()
20652     {    
20653         var i = 0;
20654         var months = this.picker().select('>.datepicker-months td', true).first();
20655         
20656         months.dom.innerHTML = '';
20657         
20658         while (i < 12) {
20659             var month = {
20660                 tag: 'span',
20661                 cls: 'month',
20662                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20663             };
20664             
20665             months.createChild(month);
20666         }
20667         
20668     },
20669     
20670     update: function()
20671     {
20672         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;
20673         
20674         if (this.date < this.startDate) {
20675             this.viewDate = new Date(this.startDate);
20676         } else if (this.date > this.endDate) {
20677             this.viewDate = new Date(this.endDate);
20678         } else {
20679             this.viewDate = new Date(this.date);
20680         }
20681         
20682         this.fill();
20683     },
20684     
20685     fill: function() 
20686     {
20687         var d = new Date(this.viewDate),
20688                 year = d.getUTCFullYear(),
20689                 month = d.getUTCMonth(),
20690                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20691                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20692                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20693                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20694                 currentDate = this.date && this.date.valueOf(),
20695                 today = this.UTCToday();
20696         
20697         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20698         
20699 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20700         
20701 //        this.picker.select('>tfoot th.today').
20702 //                                              .text(dates[this.language].today)
20703 //                                              .toggle(this.todayBtn !== false);
20704     
20705         this.updateNavArrows();
20706         this.fillMonths();
20707                                                 
20708         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20709         
20710         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20711          
20712         prevMonth.setUTCDate(day);
20713         
20714         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20715         
20716         var nextMonth = new Date(prevMonth);
20717         
20718         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20719         
20720         nextMonth = nextMonth.valueOf();
20721         
20722         var fillMonths = false;
20723         
20724         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20725         
20726         while(prevMonth.valueOf() <= nextMonth) {
20727             var clsName = '';
20728             
20729             if (prevMonth.getUTCDay() === this.weekStart) {
20730                 if(fillMonths){
20731                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20732                 }
20733                     
20734                 fillMonths = {
20735                     tag: 'tr',
20736                     cn: []
20737                 };
20738                 
20739                 if(this.calendarWeeks){
20740                     // ISO 8601: First week contains first thursday.
20741                     // ISO also states week starts on Monday, but we can be more abstract here.
20742                     var
20743                     // Start of current week: based on weekstart/current date
20744                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20745                     // Thursday of this week
20746                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20747                     // First Thursday of year, year from thursday
20748                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20749                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20750                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20751                     
20752                     fillMonths.cn.push({
20753                         tag: 'td',
20754                         cls: 'cw',
20755                         html: calWeek
20756                     });
20757                 }
20758             }
20759             
20760             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20761                 clsName += ' old';
20762             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20763                 clsName += ' new';
20764             }
20765             if (this.todayHighlight &&
20766                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20767                 prevMonth.getUTCMonth() == today.getMonth() &&
20768                 prevMonth.getUTCDate() == today.getDate()) {
20769                 clsName += ' today';
20770             }
20771             
20772             if (currentDate && prevMonth.valueOf() === currentDate) {
20773                 clsName += ' active';
20774             }
20775             
20776             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20777                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20778                     clsName += ' disabled';
20779             }
20780             
20781             fillMonths.cn.push({
20782                 tag: 'td',
20783                 cls: 'day ' + clsName,
20784                 html: prevMonth.getDate()
20785             });
20786             
20787             prevMonth.setDate(prevMonth.getDate()+1);
20788         }
20789           
20790         var currentYear = this.date && this.date.getUTCFullYear();
20791         var currentMonth = this.date && this.date.getUTCMonth();
20792         
20793         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20794         
20795         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20796             v.removeClass('active');
20797             
20798             if(currentYear === year && k === currentMonth){
20799                 v.addClass('active');
20800             }
20801             
20802             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20803                 v.addClass('disabled');
20804             }
20805             
20806         });
20807         
20808         
20809         year = parseInt(year/10, 10) * 10;
20810         
20811         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20812         
20813         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20814         
20815         year -= 1;
20816         for (var i = -1; i < 11; i++) {
20817             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20818                 tag: 'span',
20819                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20820                 html: year
20821             });
20822             
20823             year += 1;
20824         }
20825     },
20826     
20827     showMode: function(dir) 
20828     {
20829         if (dir) {
20830             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20831         }
20832         
20833         Roo.each(this.picker().select('>div',true).elements, function(v){
20834             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20835             v.hide();
20836         });
20837         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20838     },
20839     
20840     place: function()
20841     {
20842         if(this.isInline) {
20843             return;
20844         }
20845         
20846         this.picker().removeClass(['bottom', 'top']);
20847         
20848         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20849             /*
20850              * place to the top of element!
20851              *
20852              */
20853             
20854             this.picker().addClass('top');
20855             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20856             
20857             return;
20858         }
20859         
20860         this.picker().addClass('bottom');
20861         
20862         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20863     },
20864     
20865     parseDate : function(value)
20866     {
20867         if(!value || value instanceof Date){
20868             return value;
20869         }
20870         var v = Date.parseDate(value, this.format);
20871         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20872             v = Date.parseDate(value, 'Y-m-d');
20873         }
20874         if(!v && this.altFormats){
20875             if(!this.altFormatsArray){
20876                 this.altFormatsArray = this.altFormats.split("|");
20877             }
20878             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20879                 v = Date.parseDate(value, this.altFormatsArray[i]);
20880             }
20881         }
20882         return v;
20883     },
20884     
20885     formatDate : function(date, fmt)
20886     {   
20887         return (!date || !(date instanceof Date)) ?
20888         date : date.dateFormat(fmt || this.format);
20889     },
20890     
20891     onFocus : function()
20892     {
20893         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20894         this.showPopup();
20895     },
20896     
20897     onBlur : function()
20898     {
20899         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20900         
20901         var d = this.inputEl().getValue();
20902         
20903         this.setValue(d);
20904                 
20905         this.hidePopup();
20906     },
20907     
20908     showPopup : function()
20909     {
20910         this.picker().show();
20911         this.update();
20912         this.place();
20913         
20914         this.fireEvent('showpopup', this, this.date);
20915     },
20916     
20917     hidePopup : function()
20918     {
20919         if(this.isInline) {
20920             return;
20921         }
20922         this.picker().hide();
20923         this.viewMode = this.startViewMode;
20924         this.showMode();
20925         
20926         this.fireEvent('hidepopup', this, this.date);
20927         
20928     },
20929     
20930     onMousedown: function(e)
20931     {
20932         e.stopPropagation();
20933         e.preventDefault();
20934     },
20935     
20936     keyup: function(e)
20937     {
20938         Roo.bootstrap.DateField.superclass.keyup.call(this);
20939         this.update();
20940     },
20941
20942     setValue: function(v)
20943     {
20944         if(this.fireEvent('beforeselect', this, v) !== false){
20945             var d = new Date(this.parseDate(v) ).clearTime();
20946         
20947             if(isNaN(d.getTime())){
20948                 this.date = this.viewDate = '';
20949                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20950                 return;
20951             }
20952
20953             v = this.formatDate(d);
20954
20955             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20956
20957             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20958
20959             this.update();
20960
20961             this.fireEvent('select', this, this.date);
20962         }
20963     },
20964     
20965     getValue: function()
20966     {
20967         return this.formatDate(this.date);
20968     },
20969     
20970     fireKey: function(e)
20971     {
20972         if (!this.picker().isVisible()){
20973             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20974                 this.showPopup();
20975             }
20976             return;
20977         }
20978         
20979         var dateChanged = false,
20980         dir, day, month,
20981         newDate, newViewDate;
20982         
20983         switch(e.keyCode){
20984             case 27: // escape
20985                 this.hidePopup();
20986                 e.preventDefault();
20987                 break;
20988             case 37: // left
20989             case 39: // right
20990                 if (!this.keyboardNavigation) {
20991                     break;
20992                 }
20993                 dir = e.keyCode == 37 ? -1 : 1;
20994                 
20995                 if (e.ctrlKey){
20996                     newDate = this.moveYear(this.date, dir);
20997                     newViewDate = this.moveYear(this.viewDate, dir);
20998                 } else if (e.shiftKey){
20999                     newDate = this.moveMonth(this.date, dir);
21000                     newViewDate = this.moveMonth(this.viewDate, dir);
21001                 } else {
21002                     newDate = new Date(this.date);
21003                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21004                     newViewDate = new Date(this.viewDate);
21005                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21006                 }
21007                 if (this.dateWithinRange(newDate)){
21008                     this.date = newDate;
21009                     this.viewDate = newViewDate;
21010                     this.setValue(this.formatDate(this.date));
21011 //                    this.update();
21012                     e.preventDefault();
21013                     dateChanged = true;
21014                 }
21015                 break;
21016             case 38: // up
21017             case 40: // down
21018                 if (!this.keyboardNavigation) {
21019                     break;
21020                 }
21021                 dir = e.keyCode == 38 ? -1 : 1;
21022                 if (e.ctrlKey){
21023                     newDate = this.moveYear(this.date, dir);
21024                     newViewDate = this.moveYear(this.viewDate, dir);
21025                 } else if (e.shiftKey){
21026                     newDate = this.moveMonth(this.date, dir);
21027                     newViewDate = this.moveMonth(this.viewDate, dir);
21028                 } else {
21029                     newDate = new Date(this.date);
21030                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21031                     newViewDate = new Date(this.viewDate);
21032                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21033                 }
21034                 if (this.dateWithinRange(newDate)){
21035                     this.date = newDate;
21036                     this.viewDate = newViewDate;
21037                     this.setValue(this.formatDate(this.date));
21038 //                    this.update();
21039                     e.preventDefault();
21040                     dateChanged = true;
21041                 }
21042                 break;
21043             case 13: // enter
21044                 this.setValue(this.formatDate(this.date));
21045                 this.hidePopup();
21046                 e.preventDefault();
21047                 break;
21048             case 9: // tab
21049                 this.setValue(this.formatDate(this.date));
21050                 this.hidePopup();
21051                 break;
21052             case 16: // shift
21053             case 17: // ctrl
21054             case 18: // alt
21055                 break;
21056             default :
21057                 this.hidePopup();
21058                 
21059         }
21060     },
21061     
21062     
21063     onClick: function(e) 
21064     {
21065         e.stopPropagation();
21066         e.preventDefault();
21067         
21068         var target = e.getTarget();
21069         
21070         if(target.nodeName.toLowerCase() === 'i'){
21071             target = Roo.get(target).dom.parentNode;
21072         }
21073         
21074         var nodeName = target.nodeName;
21075         var className = target.className;
21076         var html = target.innerHTML;
21077         //Roo.log(nodeName);
21078         
21079         switch(nodeName.toLowerCase()) {
21080             case 'th':
21081                 switch(className) {
21082                     case 'switch':
21083                         this.showMode(1);
21084                         break;
21085                     case 'prev':
21086                     case 'next':
21087                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21088                         switch(this.viewMode){
21089                                 case 0:
21090                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21091                                         break;
21092                                 case 1:
21093                                 case 2:
21094                                         this.viewDate = this.moveYear(this.viewDate, dir);
21095                                         break;
21096                         }
21097                         this.fill();
21098                         break;
21099                     case 'today':
21100                         var date = new Date();
21101                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21102 //                        this.fill()
21103                         this.setValue(this.formatDate(this.date));
21104                         
21105                         this.hidePopup();
21106                         break;
21107                 }
21108                 break;
21109             case 'span':
21110                 if (className.indexOf('disabled') < 0) {
21111                     this.viewDate.setUTCDate(1);
21112                     if (className.indexOf('month') > -1) {
21113                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21114                     } else {
21115                         var year = parseInt(html, 10) || 0;
21116                         this.viewDate.setUTCFullYear(year);
21117                         
21118                     }
21119                     
21120                     if(this.singleMode){
21121                         this.setValue(this.formatDate(this.viewDate));
21122                         this.hidePopup();
21123                         return;
21124                     }
21125                     
21126                     this.showMode(-1);
21127                     this.fill();
21128                 }
21129                 break;
21130                 
21131             case 'td':
21132                 //Roo.log(className);
21133                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21134                     var day = parseInt(html, 10) || 1;
21135                     var year = this.viewDate.getUTCFullYear(),
21136                         month = this.viewDate.getUTCMonth();
21137
21138                     if (className.indexOf('old') > -1) {
21139                         if(month === 0 ){
21140                             month = 11;
21141                             year -= 1;
21142                         }else{
21143                             month -= 1;
21144                         }
21145                     } else if (className.indexOf('new') > -1) {
21146                         if (month == 11) {
21147                             month = 0;
21148                             year += 1;
21149                         } else {
21150                             month += 1;
21151                         }
21152                     }
21153                     //Roo.log([year,month,day]);
21154                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21155                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21156 //                    this.fill();
21157                     //Roo.log(this.formatDate(this.date));
21158                     this.setValue(this.formatDate(this.date));
21159                     this.hidePopup();
21160                 }
21161                 break;
21162         }
21163     },
21164     
21165     setStartDate: function(startDate)
21166     {
21167         this.startDate = startDate || -Infinity;
21168         if (this.startDate !== -Infinity) {
21169             this.startDate = this.parseDate(this.startDate);
21170         }
21171         this.update();
21172         this.updateNavArrows();
21173     },
21174
21175     setEndDate: function(endDate)
21176     {
21177         this.endDate = endDate || Infinity;
21178         if (this.endDate !== Infinity) {
21179             this.endDate = this.parseDate(this.endDate);
21180         }
21181         this.update();
21182         this.updateNavArrows();
21183     },
21184     
21185     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21186     {
21187         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21188         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21189             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21190         }
21191         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21192             return parseInt(d, 10);
21193         });
21194         this.update();
21195         this.updateNavArrows();
21196     },
21197     
21198     updateNavArrows: function() 
21199     {
21200         if(this.singleMode){
21201             return;
21202         }
21203         
21204         var d = new Date(this.viewDate),
21205         year = d.getUTCFullYear(),
21206         month = d.getUTCMonth();
21207         
21208         Roo.each(this.picker().select('.prev', true).elements, function(v){
21209             v.show();
21210             switch (this.viewMode) {
21211                 case 0:
21212
21213                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21214                         v.hide();
21215                     }
21216                     break;
21217                 case 1:
21218                 case 2:
21219                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21220                         v.hide();
21221                     }
21222                     break;
21223             }
21224         });
21225         
21226         Roo.each(this.picker().select('.next', true).elements, function(v){
21227             v.show();
21228             switch (this.viewMode) {
21229                 case 0:
21230
21231                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21232                         v.hide();
21233                     }
21234                     break;
21235                 case 1:
21236                 case 2:
21237                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21238                         v.hide();
21239                     }
21240                     break;
21241             }
21242         })
21243     },
21244     
21245     moveMonth: function(date, dir)
21246     {
21247         if (!dir) {
21248             return date;
21249         }
21250         var new_date = new Date(date.valueOf()),
21251         day = new_date.getUTCDate(),
21252         month = new_date.getUTCMonth(),
21253         mag = Math.abs(dir),
21254         new_month, test;
21255         dir = dir > 0 ? 1 : -1;
21256         if (mag == 1){
21257             test = dir == -1
21258             // If going back one month, make sure month is not current month
21259             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21260             ? function(){
21261                 return new_date.getUTCMonth() == month;
21262             }
21263             // If going forward one month, make sure month is as expected
21264             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21265             : function(){
21266                 return new_date.getUTCMonth() != new_month;
21267             };
21268             new_month = month + dir;
21269             new_date.setUTCMonth(new_month);
21270             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21271             if (new_month < 0 || new_month > 11) {
21272                 new_month = (new_month + 12) % 12;
21273             }
21274         } else {
21275             // For magnitudes >1, move one month at a time...
21276             for (var i=0; i<mag; i++) {
21277                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21278                 new_date = this.moveMonth(new_date, dir);
21279             }
21280             // ...then reset the day, keeping it in the new month
21281             new_month = new_date.getUTCMonth();
21282             new_date.setUTCDate(day);
21283             test = function(){
21284                 return new_month != new_date.getUTCMonth();
21285             };
21286         }
21287         // Common date-resetting loop -- if date is beyond end of month, make it
21288         // end of month
21289         while (test()){
21290             new_date.setUTCDate(--day);
21291             new_date.setUTCMonth(new_month);
21292         }
21293         return new_date;
21294     },
21295
21296     moveYear: function(date, dir)
21297     {
21298         return this.moveMonth(date, dir*12);
21299     },
21300
21301     dateWithinRange: function(date)
21302     {
21303         return date >= this.startDate && date <= this.endDate;
21304     },
21305
21306     
21307     remove: function() 
21308     {
21309         this.picker().remove();
21310     },
21311     
21312     validateValue : function(value)
21313     {
21314         if(this.getVisibilityEl().hasClass('hidden')){
21315             return true;
21316         }
21317         
21318         if(value.length < 1)  {
21319             if(this.allowBlank){
21320                 return true;
21321             }
21322             return false;
21323         }
21324         
21325         if(value.length < this.minLength){
21326             return false;
21327         }
21328         if(value.length > this.maxLength){
21329             return false;
21330         }
21331         if(this.vtype){
21332             var vt = Roo.form.VTypes;
21333             if(!vt[this.vtype](value, this)){
21334                 return false;
21335             }
21336         }
21337         if(typeof this.validator == "function"){
21338             var msg = this.validator(value);
21339             if(msg !== true){
21340                 return false;
21341             }
21342         }
21343         
21344         if(this.regex && !this.regex.test(value)){
21345             return false;
21346         }
21347         
21348         if(typeof(this.parseDate(value)) == 'undefined'){
21349             return false;
21350         }
21351         
21352         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21353             return false;
21354         }      
21355         
21356         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21357             return false;
21358         } 
21359         
21360         
21361         return true;
21362     },
21363     
21364     reset : function()
21365     {
21366         this.date = this.viewDate = '';
21367         
21368         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21369     }
21370    
21371 });
21372
21373 Roo.apply(Roo.bootstrap.DateField,  {
21374     
21375     head : {
21376         tag: 'thead',
21377         cn: [
21378         {
21379             tag: 'tr',
21380             cn: [
21381             {
21382                 tag: 'th',
21383                 cls: 'prev',
21384                 html: '<i class="fa fa-arrow-left"/>'
21385             },
21386             {
21387                 tag: 'th',
21388                 cls: 'switch',
21389                 colspan: '5'
21390             },
21391             {
21392                 tag: 'th',
21393                 cls: 'next',
21394                 html: '<i class="fa fa-arrow-right"/>'
21395             }
21396
21397             ]
21398         }
21399         ]
21400     },
21401     
21402     content : {
21403         tag: 'tbody',
21404         cn: [
21405         {
21406             tag: 'tr',
21407             cn: [
21408             {
21409                 tag: 'td',
21410                 colspan: '7'
21411             }
21412             ]
21413         }
21414         ]
21415     },
21416     
21417     footer : {
21418         tag: 'tfoot',
21419         cn: [
21420         {
21421             tag: 'tr',
21422             cn: [
21423             {
21424                 tag: 'th',
21425                 colspan: '7',
21426                 cls: 'today'
21427             }
21428                     
21429             ]
21430         }
21431         ]
21432     },
21433     
21434     dates:{
21435         en: {
21436             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21437             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21438             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21439             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21440             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21441             today: "Today"
21442         }
21443     },
21444     
21445     modes: [
21446     {
21447         clsName: 'days',
21448         navFnc: 'Month',
21449         navStep: 1
21450     },
21451     {
21452         clsName: 'months',
21453         navFnc: 'FullYear',
21454         navStep: 1
21455     },
21456     {
21457         clsName: 'years',
21458         navFnc: 'FullYear',
21459         navStep: 10
21460     }]
21461 });
21462
21463 Roo.apply(Roo.bootstrap.DateField,  {
21464   
21465     template : {
21466         tag: 'div',
21467         cls: 'datepicker dropdown-menu roo-dynamic',
21468         cn: [
21469         {
21470             tag: 'div',
21471             cls: 'datepicker-days',
21472             cn: [
21473             {
21474                 tag: 'table',
21475                 cls: 'table-condensed',
21476                 cn:[
21477                 Roo.bootstrap.DateField.head,
21478                 {
21479                     tag: 'tbody'
21480                 },
21481                 Roo.bootstrap.DateField.footer
21482                 ]
21483             }
21484             ]
21485         },
21486         {
21487             tag: 'div',
21488             cls: 'datepicker-months',
21489             cn: [
21490             {
21491                 tag: 'table',
21492                 cls: 'table-condensed',
21493                 cn:[
21494                 Roo.bootstrap.DateField.head,
21495                 Roo.bootstrap.DateField.content,
21496                 Roo.bootstrap.DateField.footer
21497                 ]
21498             }
21499             ]
21500         },
21501         {
21502             tag: 'div',
21503             cls: 'datepicker-years',
21504             cn: [
21505             {
21506                 tag: 'table',
21507                 cls: 'table-condensed',
21508                 cn:[
21509                 Roo.bootstrap.DateField.head,
21510                 Roo.bootstrap.DateField.content,
21511                 Roo.bootstrap.DateField.footer
21512                 ]
21513             }
21514             ]
21515         }
21516         ]
21517     }
21518 });
21519
21520  
21521
21522  /*
21523  * - LGPL
21524  *
21525  * TimeField
21526  * 
21527  */
21528
21529 /**
21530  * @class Roo.bootstrap.TimeField
21531  * @extends Roo.bootstrap.Input
21532  * Bootstrap DateField class
21533  * 
21534  * 
21535  * @constructor
21536  * Create a new TimeField
21537  * @param {Object} config The config object
21538  */
21539
21540 Roo.bootstrap.TimeField = function(config){
21541     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21542     this.addEvents({
21543             /**
21544              * @event show
21545              * Fires when this field show.
21546              * @param {Roo.bootstrap.DateField} thisthis
21547              * @param {Mixed} date The date value
21548              */
21549             show : true,
21550             /**
21551              * @event show
21552              * Fires when this field hide.
21553              * @param {Roo.bootstrap.DateField} this
21554              * @param {Mixed} date The date value
21555              */
21556             hide : true,
21557             /**
21558              * @event select
21559              * Fires when select a date.
21560              * @param {Roo.bootstrap.DateField} this
21561              * @param {Mixed} date The date value
21562              */
21563             select : true
21564         });
21565 };
21566
21567 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21568     
21569     /**
21570      * @cfg {String} format
21571      * The default time format string which can be overriden for localization support.  The format must be
21572      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21573      */
21574     format : "H:i",
21575        
21576     onRender: function(ct, position)
21577     {
21578         
21579         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21580                 
21581         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21582         
21583         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21584         
21585         this.pop = this.picker().select('>.datepicker-time',true).first();
21586         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21587         
21588         this.picker().on('mousedown', this.onMousedown, this);
21589         this.picker().on('click', this.onClick, this);
21590         
21591         this.picker().addClass('datepicker-dropdown');
21592     
21593         this.fillTime();
21594         this.update();
21595             
21596         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21597         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21598         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21599         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21600         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21601         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21602
21603     },
21604     
21605     fireKey: function(e){
21606         if (!this.picker().isVisible()){
21607             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21608                 this.show();
21609             }
21610             return;
21611         }
21612
21613         e.preventDefault();
21614         
21615         switch(e.keyCode){
21616             case 27: // escape
21617                 this.hide();
21618                 break;
21619             case 37: // left
21620             case 39: // right
21621                 this.onTogglePeriod();
21622                 break;
21623             case 38: // up
21624                 this.onIncrementMinutes();
21625                 break;
21626             case 40: // down
21627                 this.onDecrementMinutes();
21628                 break;
21629             case 13: // enter
21630             case 9: // tab
21631                 this.setTime();
21632                 break;
21633         }
21634     },
21635     
21636     onClick: function(e) {
21637         e.stopPropagation();
21638         e.preventDefault();
21639     },
21640     
21641     picker : function()
21642     {
21643         return this.el.select('.datepicker', true).first();
21644     },
21645     
21646     fillTime: function()
21647     {    
21648         var time = this.pop.select('tbody', true).first();
21649         
21650         time.dom.innerHTML = '';
21651         
21652         time.createChild({
21653             tag: 'tr',
21654             cn: [
21655                 {
21656                     tag: 'td',
21657                     cn: [
21658                         {
21659                             tag: 'a',
21660                             href: '#',
21661                             cls: 'btn',
21662                             cn: [
21663                                 {
21664                                     tag: 'span',
21665                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21666                                 }
21667                             ]
21668                         } 
21669                     ]
21670                 },
21671                 {
21672                     tag: 'td',
21673                     cls: 'separator'
21674                 },
21675                 {
21676                     tag: 'td',
21677                     cn: [
21678                         {
21679                             tag: 'a',
21680                             href: '#',
21681                             cls: 'btn',
21682                             cn: [
21683                                 {
21684                                     tag: 'span',
21685                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21686                                 }
21687                             ]
21688                         }
21689                     ]
21690                 },
21691                 {
21692                     tag: 'td',
21693                     cls: 'separator'
21694                 }
21695             ]
21696         });
21697         
21698         time.createChild({
21699             tag: 'tr',
21700             cn: [
21701                 {
21702                     tag: 'td',
21703                     cn: [
21704                         {
21705                             tag: 'span',
21706                             cls: 'timepicker-hour',
21707                             html: '00'
21708                         }  
21709                     ]
21710                 },
21711                 {
21712                     tag: 'td',
21713                     cls: 'separator',
21714                     html: ':'
21715                 },
21716                 {
21717                     tag: 'td',
21718                     cn: [
21719                         {
21720                             tag: 'span',
21721                             cls: 'timepicker-minute',
21722                             html: '00'
21723                         }  
21724                     ]
21725                 },
21726                 {
21727                     tag: 'td',
21728                     cls: 'separator'
21729                 },
21730                 {
21731                     tag: 'td',
21732                     cn: [
21733                         {
21734                             tag: 'button',
21735                             type: 'button',
21736                             cls: 'btn btn-primary period',
21737                             html: 'AM'
21738                             
21739                         }
21740                     ]
21741                 }
21742             ]
21743         });
21744         
21745         time.createChild({
21746             tag: 'tr',
21747             cn: [
21748                 {
21749                     tag: 'td',
21750                     cn: [
21751                         {
21752                             tag: 'a',
21753                             href: '#',
21754                             cls: 'btn',
21755                             cn: [
21756                                 {
21757                                     tag: 'span',
21758                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21759                                 }
21760                             ]
21761                         }
21762                     ]
21763                 },
21764                 {
21765                     tag: 'td',
21766                     cls: 'separator'
21767                 },
21768                 {
21769                     tag: 'td',
21770                     cn: [
21771                         {
21772                             tag: 'a',
21773                             href: '#',
21774                             cls: 'btn',
21775                             cn: [
21776                                 {
21777                                     tag: 'span',
21778                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21779                                 }
21780                             ]
21781                         }
21782                     ]
21783                 },
21784                 {
21785                     tag: 'td',
21786                     cls: 'separator'
21787                 }
21788             ]
21789         });
21790         
21791     },
21792     
21793     update: function()
21794     {
21795         
21796         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21797         
21798         this.fill();
21799     },
21800     
21801     fill: function() 
21802     {
21803         var hours = this.time.getHours();
21804         var minutes = this.time.getMinutes();
21805         var period = 'AM';
21806         
21807         if(hours > 11){
21808             period = 'PM';
21809         }
21810         
21811         if(hours == 0){
21812             hours = 12;
21813         }
21814         
21815         
21816         if(hours > 12){
21817             hours = hours - 12;
21818         }
21819         
21820         if(hours < 10){
21821             hours = '0' + hours;
21822         }
21823         
21824         if(minutes < 10){
21825             minutes = '0' + minutes;
21826         }
21827         
21828         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21829         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21830         this.pop.select('button', true).first().dom.innerHTML = period;
21831         
21832     },
21833     
21834     place: function()
21835     {   
21836         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21837         
21838         var cls = ['bottom'];
21839         
21840         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21841             cls.pop();
21842             cls.push('top');
21843         }
21844         
21845         cls.push('right');
21846         
21847         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21848             cls.pop();
21849             cls.push('left');
21850         }
21851         
21852         this.picker().addClass(cls.join('-'));
21853         
21854         var _this = this;
21855         
21856         Roo.each(cls, function(c){
21857             if(c == 'bottom'){
21858                 _this.picker().setTop(_this.inputEl().getHeight());
21859                 return;
21860             }
21861             if(c == 'top'){
21862                 _this.picker().setTop(0 - _this.picker().getHeight());
21863                 return;
21864             }
21865             
21866             if(c == 'left'){
21867                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21868                 return;
21869             }
21870             if(c == 'right'){
21871                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21872                 return;
21873             }
21874         });
21875         
21876     },
21877   
21878     onFocus : function()
21879     {
21880         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21881         this.show();
21882     },
21883     
21884     onBlur : function()
21885     {
21886         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21887         this.hide();
21888     },
21889     
21890     show : function()
21891     {
21892         this.picker().show();
21893         this.pop.show();
21894         this.update();
21895         this.place();
21896         
21897         this.fireEvent('show', this, this.date);
21898     },
21899     
21900     hide : function()
21901     {
21902         this.picker().hide();
21903         this.pop.hide();
21904         
21905         this.fireEvent('hide', this, this.date);
21906     },
21907     
21908     setTime : function()
21909     {
21910         this.hide();
21911         this.setValue(this.time.format(this.format));
21912         
21913         this.fireEvent('select', this, this.date);
21914         
21915         
21916     },
21917     
21918     onMousedown: function(e){
21919         e.stopPropagation();
21920         e.preventDefault();
21921     },
21922     
21923     onIncrementHours: function()
21924     {
21925         Roo.log('onIncrementHours');
21926         this.time = this.time.add(Date.HOUR, 1);
21927         this.update();
21928         
21929     },
21930     
21931     onDecrementHours: function()
21932     {
21933         Roo.log('onDecrementHours');
21934         this.time = this.time.add(Date.HOUR, -1);
21935         this.update();
21936     },
21937     
21938     onIncrementMinutes: function()
21939     {
21940         Roo.log('onIncrementMinutes');
21941         this.time = this.time.add(Date.MINUTE, 1);
21942         this.update();
21943     },
21944     
21945     onDecrementMinutes: function()
21946     {
21947         Roo.log('onDecrementMinutes');
21948         this.time = this.time.add(Date.MINUTE, -1);
21949         this.update();
21950     },
21951     
21952     onTogglePeriod: function()
21953     {
21954         Roo.log('onTogglePeriod');
21955         this.time = this.time.add(Date.HOUR, 12);
21956         this.update();
21957     }
21958     
21959    
21960 });
21961
21962 Roo.apply(Roo.bootstrap.TimeField,  {
21963     
21964     content : {
21965         tag: 'tbody',
21966         cn: [
21967             {
21968                 tag: 'tr',
21969                 cn: [
21970                 {
21971                     tag: 'td',
21972                     colspan: '7'
21973                 }
21974                 ]
21975             }
21976         ]
21977     },
21978     
21979     footer : {
21980         tag: 'tfoot',
21981         cn: [
21982             {
21983                 tag: 'tr',
21984                 cn: [
21985                 {
21986                     tag: 'th',
21987                     colspan: '7',
21988                     cls: '',
21989                     cn: [
21990                         {
21991                             tag: 'button',
21992                             cls: 'btn btn-info ok',
21993                             html: 'OK'
21994                         }
21995                     ]
21996                 }
21997
21998                 ]
21999             }
22000         ]
22001     }
22002 });
22003
22004 Roo.apply(Roo.bootstrap.TimeField,  {
22005   
22006     template : {
22007         tag: 'div',
22008         cls: 'datepicker dropdown-menu',
22009         cn: [
22010             {
22011                 tag: 'div',
22012                 cls: 'datepicker-time',
22013                 cn: [
22014                 {
22015                     tag: 'table',
22016                     cls: 'table-condensed',
22017                     cn:[
22018                     Roo.bootstrap.TimeField.content,
22019                     Roo.bootstrap.TimeField.footer
22020                     ]
22021                 }
22022                 ]
22023             }
22024         ]
22025     }
22026 });
22027
22028  
22029
22030  /*
22031  * - LGPL
22032  *
22033  * MonthField
22034  * 
22035  */
22036
22037 /**
22038  * @class Roo.bootstrap.MonthField
22039  * @extends Roo.bootstrap.Input
22040  * Bootstrap MonthField class
22041  * 
22042  * @cfg {String} language default en
22043  * 
22044  * @constructor
22045  * Create a new MonthField
22046  * @param {Object} config The config object
22047  */
22048
22049 Roo.bootstrap.MonthField = function(config){
22050     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22051     
22052     this.addEvents({
22053         /**
22054          * @event show
22055          * Fires when this field show.
22056          * @param {Roo.bootstrap.MonthField} this
22057          * @param {Mixed} date The date value
22058          */
22059         show : true,
22060         /**
22061          * @event show
22062          * Fires when this field hide.
22063          * @param {Roo.bootstrap.MonthField} this
22064          * @param {Mixed} date The date value
22065          */
22066         hide : true,
22067         /**
22068          * @event select
22069          * Fires when select a date.
22070          * @param {Roo.bootstrap.MonthField} this
22071          * @param {String} oldvalue The old value
22072          * @param {String} newvalue The new value
22073          */
22074         select : true
22075     });
22076 };
22077
22078 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22079     
22080     onRender: function(ct, position)
22081     {
22082         
22083         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22084         
22085         this.language = this.language || 'en';
22086         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22087         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22088         
22089         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22090         this.isInline = false;
22091         this.isInput = true;
22092         this.component = this.el.select('.add-on', true).first() || false;
22093         this.component = (this.component && this.component.length === 0) ? false : this.component;
22094         this.hasInput = this.component && this.inputEL().length;
22095         
22096         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22097         
22098         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22099         
22100         this.picker().on('mousedown', this.onMousedown, this);
22101         this.picker().on('click', this.onClick, this);
22102         
22103         this.picker().addClass('datepicker-dropdown');
22104         
22105         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22106             v.setStyle('width', '189px');
22107         });
22108         
22109         this.fillMonths();
22110         
22111         this.update();
22112         
22113         if(this.isInline) {
22114             this.show();
22115         }
22116         
22117     },
22118     
22119     setValue: function(v, suppressEvent)
22120     {   
22121         var o = this.getValue();
22122         
22123         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22124         
22125         this.update();
22126
22127         if(suppressEvent !== true){
22128             this.fireEvent('select', this, o, v);
22129         }
22130         
22131     },
22132     
22133     getValue: function()
22134     {
22135         return this.value;
22136     },
22137     
22138     onClick: function(e) 
22139     {
22140         e.stopPropagation();
22141         e.preventDefault();
22142         
22143         var target = e.getTarget();
22144         
22145         if(target.nodeName.toLowerCase() === 'i'){
22146             target = Roo.get(target).dom.parentNode;
22147         }
22148         
22149         var nodeName = target.nodeName;
22150         var className = target.className;
22151         var html = target.innerHTML;
22152         
22153         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22154             return;
22155         }
22156         
22157         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22158         
22159         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22160         
22161         this.hide();
22162                         
22163     },
22164     
22165     picker : function()
22166     {
22167         return this.pickerEl;
22168     },
22169     
22170     fillMonths: function()
22171     {    
22172         var i = 0;
22173         var months = this.picker().select('>.datepicker-months td', true).first();
22174         
22175         months.dom.innerHTML = '';
22176         
22177         while (i < 12) {
22178             var month = {
22179                 tag: 'span',
22180                 cls: 'month',
22181                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22182             };
22183             
22184             months.createChild(month);
22185         }
22186         
22187     },
22188     
22189     update: function()
22190     {
22191         var _this = this;
22192         
22193         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22194             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22195         }
22196         
22197         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22198             e.removeClass('active');
22199             
22200             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22201                 e.addClass('active');
22202             }
22203         })
22204     },
22205     
22206     place: function()
22207     {
22208         if(this.isInline) {
22209             return;
22210         }
22211         
22212         this.picker().removeClass(['bottom', 'top']);
22213         
22214         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22215             /*
22216              * place to the top of element!
22217              *
22218              */
22219             
22220             this.picker().addClass('top');
22221             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22222             
22223             return;
22224         }
22225         
22226         this.picker().addClass('bottom');
22227         
22228         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22229     },
22230     
22231     onFocus : function()
22232     {
22233         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22234         this.show();
22235     },
22236     
22237     onBlur : function()
22238     {
22239         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22240         
22241         var d = this.inputEl().getValue();
22242         
22243         this.setValue(d);
22244                 
22245         this.hide();
22246     },
22247     
22248     show : function()
22249     {
22250         this.picker().show();
22251         this.picker().select('>.datepicker-months', true).first().show();
22252         this.update();
22253         this.place();
22254         
22255         this.fireEvent('show', this, this.date);
22256     },
22257     
22258     hide : function()
22259     {
22260         if(this.isInline) {
22261             return;
22262         }
22263         this.picker().hide();
22264         this.fireEvent('hide', this, this.date);
22265         
22266     },
22267     
22268     onMousedown: function(e)
22269     {
22270         e.stopPropagation();
22271         e.preventDefault();
22272     },
22273     
22274     keyup: function(e)
22275     {
22276         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22277         this.update();
22278     },
22279
22280     fireKey: function(e)
22281     {
22282         if (!this.picker().isVisible()){
22283             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22284                 this.show();
22285             }
22286             return;
22287         }
22288         
22289         var dir;
22290         
22291         switch(e.keyCode){
22292             case 27: // escape
22293                 this.hide();
22294                 e.preventDefault();
22295                 break;
22296             case 37: // left
22297             case 39: // right
22298                 dir = e.keyCode == 37 ? -1 : 1;
22299                 
22300                 this.vIndex = this.vIndex + dir;
22301                 
22302                 if(this.vIndex < 0){
22303                     this.vIndex = 0;
22304                 }
22305                 
22306                 if(this.vIndex > 11){
22307                     this.vIndex = 11;
22308                 }
22309                 
22310                 if(isNaN(this.vIndex)){
22311                     this.vIndex = 0;
22312                 }
22313                 
22314                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22315                 
22316                 break;
22317             case 38: // up
22318             case 40: // down
22319                 
22320                 dir = e.keyCode == 38 ? -1 : 1;
22321                 
22322                 this.vIndex = this.vIndex + dir * 4;
22323                 
22324                 if(this.vIndex < 0){
22325                     this.vIndex = 0;
22326                 }
22327                 
22328                 if(this.vIndex > 11){
22329                     this.vIndex = 11;
22330                 }
22331                 
22332                 if(isNaN(this.vIndex)){
22333                     this.vIndex = 0;
22334                 }
22335                 
22336                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22337                 break;
22338                 
22339             case 13: // enter
22340                 
22341                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22342                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22343                 }
22344                 
22345                 this.hide();
22346                 e.preventDefault();
22347                 break;
22348             case 9: // tab
22349                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22350                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22351                 }
22352                 this.hide();
22353                 break;
22354             case 16: // shift
22355             case 17: // ctrl
22356             case 18: // alt
22357                 break;
22358             default :
22359                 this.hide();
22360                 
22361         }
22362     },
22363     
22364     remove: function() 
22365     {
22366         this.picker().remove();
22367     }
22368    
22369 });
22370
22371 Roo.apply(Roo.bootstrap.MonthField,  {
22372     
22373     content : {
22374         tag: 'tbody',
22375         cn: [
22376         {
22377             tag: 'tr',
22378             cn: [
22379             {
22380                 tag: 'td',
22381                 colspan: '7'
22382             }
22383             ]
22384         }
22385         ]
22386     },
22387     
22388     dates:{
22389         en: {
22390             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22391             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22392         }
22393     }
22394 });
22395
22396 Roo.apply(Roo.bootstrap.MonthField,  {
22397   
22398     template : {
22399         tag: 'div',
22400         cls: 'datepicker dropdown-menu roo-dynamic',
22401         cn: [
22402             {
22403                 tag: 'div',
22404                 cls: 'datepicker-months',
22405                 cn: [
22406                 {
22407                     tag: 'table',
22408                     cls: 'table-condensed',
22409                     cn:[
22410                         Roo.bootstrap.DateField.content
22411                     ]
22412                 }
22413                 ]
22414             }
22415         ]
22416     }
22417 });
22418
22419  
22420
22421  
22422  /*
22423  * - LGPL
22424  *
22425  * CheckBox
22426  * 
22427  */
22428
22429 /**
22430  * @class Roo.bootstrap.CheckBox
22431  * @extends Roo.bootstrap.Input
22432  * Bootstrap CheckBox class
22433  * 
22434  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22435  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22436  * @cfg {String} boxLabel The text that appears beside the checkbox
22437  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22438  * @cfg {Boolean} checked initnal the element
22439  * @cfg {Boolean} inline inline the element (default false)
22440  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22441  * @cfg {String} tooltip label tooltip
22442  * 
22443  * @constructor
22444  * Create a new CheckBox
22445  * @param {Object} config The config object
22446  */
22447
22448 Roo.bootstrap.CheckBox = function(config){
22449     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22450    
22451     this.addEvents({
22452         /**
22453         * @event check
22454         * Fires when the element is checked or unchecked.
22455         * @param {Roo.bootstrap.CheckBox} this This input
22456         * @param {Boolean} checked The new checked value
22457         */
22458        check : true,
22459        /**
22460         * @event click
22461         * Fires when the element is click.
22462         * @param {Roo.bootstrap.CheckBox} this This input
22463         */
22464        click : true
22465     });
22466     
22467 };
22468
22469 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22470   
22471     inputType: 'checkbox',
22472     inputValue: 1,
22473     valueOff: 0,
22474     boxLabel: false,
22475     checked: false,
22476     weight : false,
22477     inline: false,
22478     tooltip : '',
22479     
22480     // checkbox success does not make any sense really.. 
22481     invalidClass : "",
22482     validClass : "",
22483     
22484     
22485     getAutoCreate : function()
22486     {
22487         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22488         
22489         var id = Roo.id();
22490         
22491         var cfg = {};
22492         
22493         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22494         
22495         if(this.inline){
22496             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22497         }
22498         
22499         var input =  {
22500             tag: 'input',
22501             id : id,
22502             type : this.inputType,
22503             value : this.inputValue,
22504             cls : 'roo-' + this.inputType, //'form-box',
22505             placeholder : this.placeholder || ''
22506             
22507         };
22508         
22509         if(this.inputType != 'radio'){
22510             var hidden =  {
22511                 tag: 'input',
22512                 type : 'hidden',
22513                 cls : 'roo-hidden-value',
22514                 value : this.checked ? this.inputValue : this.valueOff
22515             };
22516         }
22517         
22518             
22519         if (this.weight) { // Validity check?
22520             cfg.cls += " " + this.inputType + "-" + this.weight;
22521         }
22522         
22523         if (this.disabled) {
22524             input.disabled=true;
22525         }
22526         
22527         if(this.checked){
22528             input.checked = this.checked;
22529         }
22530         
22531         if (this.name) {
22532             
22533             input.name = this.name;
22534             
22535             if(this.inputType != 'radio'){
22536                 hidden.name = this.name;
22537                 input.name = '_hidden_' + this.name;
22538             }
22539         }
22540         
22541         if (this.size) {
22542             input.cls += ' input-' + this.size;
22543         }
22544         
22545         var settings=this;
22546         
22547         ['xs','sm','md','lg'].map(function(size){
22548             if (settings[size]) {
22549                 cfg.cls += ' col-' + size + '-' + settings[size];
22550             }
22551         });
22552         
22553         var inputblock = input;
22554          
22555         if (this.before || this.after) {
22556             
22557             inputblock = {
22558                 cls : 'input-group',
22559                 cn :  [] 
22560             };
22561             
22562             if (this.before) {
22563                 inputblock.cn.push({
22564                     tag :'span',
22565                     cls : 'input-group-addon',
22566                     html : this.before
22567                 });
22568             }
22569             
22570             inputblock.cn.push(input);
22571             
22572             if(this.inputType != 'radio'){
22573                 inputblock.cn.push(hidden);
22574             }
22575             
22576             if (this.after) {
22577                 inputblock.cn.push({
22578                     tag :'span',
22579                     cls : 'input-group-addon',
22580                     html : this.after
22581                 });
22582             }
22583             
22584         }
22585         var boxLabelCfg = false;
22586         
22587         if(this.boxLabel){
22588            
22589             boxLabelCfg = {
22590                 tag: 'label',
22591                 //'for': id, // box label is handled by onclick - so no for...
22592                 cls: 'box-label',
22593                 html: this.boxLabel
22594             };
22595             if(this.tooltip){
22596                 boxLabelCfg.tooltip = this.tooltip;
22597             }
22598              
22599         }
22600         
22601         
22602         if (align ==='left' && this.fieldLabel.length) {
22603 //                Roo.log("left and has label");
22604             cfg.cn = [
22605                 {
22606                     tag: 'label',
22607                     'for' :  id,
22608                     cls : 'control-label',
22609                     html : this.fieldLabel
22610                 },
22611                 {
22612                     cls : "", 
22613                     cn: [
22614                         inputblock
22615                     ]
22616                 }
22617             ];
22618             
22619             if (boxLabelCfg) {
22620                 cfg.cn[1].cn.push(boxLabelCfg);
22621             }
22622             
22623             if(this.labelWidth > 12){
22624                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22625             }
22626             
22627             if(this.labelWidth < 13 && this.labelmd == 0){
22628                 this.labelmd = this.labelWidth;
22629             }
22630             
22631             if(this.labellg > 0){
22632                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22633                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22634             }
22635             
22636             if(this.labelmd > 0){
22637                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22638                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22639             }
22640             
22641             if(this.labelsm > 0){
22642                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22643                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22644             }
22645             
22646             if(this.labelxs > 0){
22647                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22648                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22649             }
22650             
22651         } else if ( this.fieldLabel.length) {
22652 //                Roo.log(" label");
22653                 cfg.cn = [
22654                    
22655                     {
22656                         tag: this.boxLabel ? 'span' : 'label',
22657                         'for': id,
22658                         cls: 'control-label box-input-label',
22659                         //cls : 'input-group-addon',
22660                         html : this.fieldLabel
22661                     },
22662                     
22663                     inputblock
22664                     
22665                 ];
22666                 if (boxLabelCfg) {
22667                     cfg.cn.push(boxLabelCfg);
22668                 }
22669
22670         } else {
22671             
22672 //                Roo.log(" no label && no align");
22673                 cfg.cn = [  inputblock ] ;
22674                 if (boxLabelCfg) {
22675                     cfg.cn.push(boxLabelCfg);
22676                 }
22677
22678                 
22679         }
22680         
22681        
22682         
22683         if(this.inputType != 'radio'){
22684             cfg.cn.push(hidden);
22685         }
22686         
22687         return cfg;
22688         
22689     },
22690     
22691     /**
22692      * return the real input element.
22693      */
22694     inputEl: function ()
22695     {
22696         return this.el.select('input.roo-' + this.inputType,true).first();
22697     },
22698     hiddenEl: function ()
22699     {
22700         return this.el.select('input.roo-hidden-value',true).first();
22701     },
22702     
22703     labelEl: function()
22704     {
22705         return this.el.select('label.control-label',true).first();
22706     },
22707     /* depricated... */
22708     
22709     label: function()
22710     {
22711         return this.labelEl();
22712     },
22713     
22714     boxLabelEl: function()
22715     {
22716         return this.el.select('label.box-label',true).first();
22717     },
22718     
22719     initEvents : function()
22720     {
22721 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22722         
22723         this.inputEl().on('click', this.onClick,  this);
22724         
22725         if (this.boxLabel) { 
22726             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22727         }
22728         
22729         this.startValue = this.getValue();
22730         
22731         if(this.groupId){
22732             Roo.bootstrap.CheckBox.register(this);
22733         }
22734     },
22735     
22736     onClick : function(e)
22737     {   
22738         if(this.fireEvent('click', this, e) !== false){
22739             this.setChecked(!this.checked);
22740         }
22741         
22742     },
22743     
22744     setChecked : function(state,suppressEvent)
22745     {
22746         this.startValue = this.getValue();
22747
22748         if(this.inputType == 'radio'){
22749             
22750             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22751                 e.dom.checked = false;
22752             });
22753             
22754             this.inputEl().dom.checked = true;
22755             
22756             this.inputEl().dom.value = this.inputValue;
22757             
22758             if(suppressEvent !== true){
22759                 this.fireEvent('check', this, true);
22760             }
22761             
22762             this.validate();
22763             
22764             return;
22765         }
22766         
22767         this.checked = state;
22768         
22769         this.inputEl().dom.checked = state;
22770         
22771         
22772         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22773         
22774         if(suppressEvent !== true){
22775             this.fireEvent('check', this, state);
22776         }
22777         
22778         this.validate();
22779     },
22780     
22781     getValue : function()
22782     {
22783         if(this.inputType == 'radio'){
22784             return this.getGroupValue();
22785         }
22786         
22787         return this.hiddenEl().dom.value;
22788         
22789     },
22790     
22791     getGroupValue : function()
22792     {
22793         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22794             return '';
22795         }
22796         
22797         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22798     },
22799     
22800     setValue : function(v,suppressEvent)
22801     {
22802         if(this.inputType == 'radio'){
22803             this.setGroupValue(v, suppressEvent);
22804             return;
22805         }
22806         
22807         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22808         
22809         this.validate();
22810     },
22811     
22812     setGroupValue : function(v, suppressEvent)
22813     {
22814         this.startValue = this.getValue();
22815         
22816         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22817             e.dom.checked = false;
22818             
22819             if(e.dom.value == v){
22820                 e.dom.checked = true;
22821             }
22822         });
22823         
22824         if(suppressEvent !== true){
22825             this.fireEvent('check', this, true);
22826         }
22827
22828         this.validate();
22829         
22830         return;
22831     },
22832     
22833     validate : function()
22834     {
22835         if(this.getVisibilityEl().hasClass('hidden')){
22836             return true;
22837         }
22838         
22839         if(
22840                 this.disabled || 
22841                 (this.inputType == 'radio' && this.validateRadio()) ||
22842                 (this.inputType == 'checkbox' && this.validateCheckbox())
22843         ){
22844             this.markValid();
22845             return true;
22846         }
22847         
22848         this.markInvalid();
22849         return false;
22850     },
22851     
22852     validateRadio : function()
22853     {
22854         if(this.getVisibilityEl().hasClass('hidden')){
22855             return true;
22856         }
22857         
22858         if(this.allowBlank){
22859             return true;
22860         }
22861         
22862         var valid = false;
22863         
22864         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22865             if(!e.dom.checked){
22866                 return;
22867             }
22868             
22869             valid = true;
22870             
22871             return false;
22872         });
22873         
22874         return valid;
22875     },
22876     
22877     validateCheckbox : function()
22878     {
22879         if(!this.groupId){
22880             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22881             //return (this.getValue() == this.inputValue) ? true : false;
22882         }
22883         
22884         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22885         
22886         if(!group){
22887             return false;
22888         }
22889         
22890         var r = false;
22891         
22892         for(var i in group){
22893             if(group[i].el.isVisible(true)){
22894                 r = false;
22895                 break;
22896             }
22897             
22898             r = true;
22899         }
22900         
22901         for(var i in group){
22902             if(r){
22903                 break;
22904             }
22905             
22906             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22907         }
22908         
22909         return r;
22910     },
22911     
22912     /**
22913      * Mark this field as valid
22914      */
22915     markValid : function()
22916     {
22917         var _this = this;
22918         
22919         this.fireEvent('valid', this);
22920         
22921         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22922         
22923         if(this.groupId){
22924             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22925         }
22926         
22927         if(label){
22928             label.markValid();
22929         }
22930
22931         if(this.inputType == 'radio'){
22932             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22933                 var fg = e.findParent('.form-group', false, true);
22934                 if (Roo.bootstrap.version == 3) {
22935                     fg.removeClass([_this.invalidClass, _this.validClass]);
22936                     fg.addClass(_this.validClass);
22937                 } else {
22938                     fg.removeClass(['is-valid', 'is-invalid']);
22939                     fg.addClass('is-valid');
22940                 }
22941             });
22942             
22943             return;
22944         }
22945
22946         if(!this.groupId){
22947             var fg = this.el.findParent('.form-group', false, true);
22948             if (Roo.bootstrap.version == 3) {
22949                 fg.removeClass([this.invalidClass, this.validClass]);
22950                 fg.addClass(this.validClass);
22951             } else {
22952                 fg.removeClass(['is-valid', 'is-invalid']);
22953                 fg.addClass('is-valid');
22954             }
22955             return;
22956         }
22957         
22958         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22959         
22960         if(!group){
22961             return;
22962         }
22963         
22964         for(var i in group){
22965             var fg = group[i].el.findParent('.form-group', false, true);
22966             if (Roo.bootstrap.version == 3) {
22967                 fg.removeClass([this.invalidClass, this.validClass]);
22968                 fg.addClass(this.validClass);
22969             } else {
22970                 fg.removeClass(['is-valid', 'is-invalid']);
22971                 fg.addClass('is-valid');
22972             }
22973         }
22974     },
22975     
22976      /**
22977      * Mark this field as invalid
22978      * @param {String} msg The validation message
22979      */
22980     markInvalid : function(msg)
22981     {
22982         if(this.allowBlank){
22983             return;
22984         }
22985         
22986         var _this = this;
22987         
22988         this.fireEvent('invalid', this, msg);
22989         
22990         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22991         
22992         if(this.groupId){
22993             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22994         }
22995         
22996         if(label){
22997             label.markInvalid();
22998         }
22999             
23000         if(this.inputType == 'radio'){
23001             
23002             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23003                 var fg = e.findParent('.form-group', false, true);
23004                 if (Roo.bootstrap.version == 3) {
23005                     fg.removeClass([_this.invalidClass, _this.validClass]);
23006                     fg.addClass(_this.invalidClass);
23007                 } else {
23008                     fg.removeClass(['is-invalid', 'is-valid']);
23009                     fg.addClass('is-invalid');
23010                 }
23011             });
23012             
23013             return;
23014         }
23015         
23016         if(!this.groupId){
23017             var fg = this.el.findParent('.form-group', false, true);
23018             if (Roo.bootstrap.version == 3) {
23019                 fg.removeClass([_this.invalidClass, _this.validClass]);
23020                 fg.addClass(_this.invalidClass);
23021             } else {
23022                 fg.removeClass(['is-invalid', 'is-valid']);
23023                 fg.addClass('is-invalid');
23024             }
23025             return;
23026         }
23027         
23028         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23029         
23030         if(!group){
23031             return;
23032         }
23033         
23034         for(var i in group){
23035             var fg = group[i].el.findParent('.form-group', false, true);
23036             if (Roo.bootstrap.version == 3) {
23037                 fg.removeClass([_this.invalidClass, _this.validClass]);
23038                 fg.addClass(_this.invalidClass);
23039             } else {
23040                 fg.removeClass(['is-invalid', 'is-valid']);
23041                 fg.addClass('is-invalid');
23042             }
23043         }
23044         
23045     },
23046     
23047     clearInvalid : function()
23048     {
23049         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23050         
23051         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23052         
23053         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23054         
23055         if (label && label.iconEl) {
23056             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23057             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23058         }
23059     },
23060     
23061     disable : function()
23062     {
23063         if(this.inputType != 'radio'){
23064             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23065             return;
23066         }
23067         
23068         var _this = this;
23069         
23070         if(this.rendered){
23071             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23072                 _this.getActionEl().addClass(this.disabledClass);
23073                 e.dom.disabled = true;
23074             });
23075         }
23076         
23077         this.disabled = true;
23078         this.fireEvent("disable", this);
23079         return this;
23080     },
23081
23082     enable : function()
23083     {
23084         if(this.inputType != 'radio'){
23085             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23086             return;
23087         }
23088         
23089         var _this = this;
23090         
23091         if(this.rendered){
23092             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23093                 _this.getActionEl().removeClass(this.disabledClass);
23094                 e.dom.disabled = false;
23095             });
23096         }
23097         
23098         this.disabled = false;
23099         this.fireEvent("enable", this);
23100         return this;
23101     },
23102     
23103     setBoxLabel : function(v)
23104     {
23105         this.boxLabel = v;
23106         
23107         if(this.rendered){
23108             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23109         }
23110     }
23111
23112 });
23113
23114 Roo.apply(Roo.bootstrap.CheckBox, {
23115     
23116     groups: {},
23117     
23118      /**
23119     * register a CheckBox Group
23120     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23121     */
23122     register : function(checkbox)
23123     {
23124         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23125             this.groups[checkbox.groupId] = {};
23126         }
23127         
23128         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23129             return;
23130         }
23131         
23132         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23133         
23134     },
23135     /**
23136     * fetch a CheckBox Group based on the group ID
23137     * @param {string} the group ID
23138     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23139     */
23140     get: function(groupId) {
23141         if (typeof(this.groups[groupId]) == 'undefined') {
23142             return false;
23143         }
23144         
23145         return this.groups[groupId] ;
23146     }
23147     
23148     
23149 });
23150 /*
23151  * - LGPL
23152  *
23153  * RadioItem
23154  * 
23155  */
23156
23157 /**
23158  * @class Roo.bootstrap.Radio
23159  * @extends Roo.bootstrap.Component
23160  * Bootstrap Radio class
23161  * @cfg {String} boxLabel - the label associated
23162  * @cfg {String} value - the value of radio
23163  * 
23164  * @constructor
23165  * Create a new Radio
23166  * @param {Object} config The config object
23167  */
23168 Roo.bootstrap.Radio = function(config){
23169     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23170     
23171 };
23172
23173 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23174     
23175     boxLabel : '',
23176     
23177     value : '',
23178     
23179     getAutoCreate : function()
23180     {
23181         var cfg = {
23182             tag : 'div',
23183             cls : 'form-group radio',
23184             cn : [
23185                 {
23186                     tag : 'label',
23187                     cls : 'box-label',
23188                     html : this.boxLabel
23189                 }
23190             ]
23191         };
23192         
23193         return cfg;
23194     },
23195     
23196     initEvents : function() 
23197     {
23198         this.parent().register(this);
23199         
23200         this.el.on('click', this.onClick, this);
23201         
23202     },
23203     
23204     onClick : function(e)
23205     {
23206         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23207             this.setChecked(true);
23208         }
23209     },
23210     
23211     setChecked : function(state, suppressEvent)
23212     {
23213         this.parent().setValue(this.value, suppressEvent);
23214         
23215     },
23216     
23217     setBoxLabel : function(v)
23218     {
23219         this.boxLabel = v;
23220         
23221         if(this.rendered){
23222             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23223         }
23224     }
23225     
23226 });
23227  
23228
23229  /*
23230  * - LGPL
23231  *
23232  * Input
23233  * 
23234  */
23235
23236 /**
23237  * @class Roo.bootstrap.SecurePass
23238  * @extends Roo.bootstrap.Input
23239  * Bootstrap SecurePass class
23240  *
23241  * 
23242  * @constructor
23243  * Create a new SecurePass
23244  * @param {Object} config The config object
23245  */
23246  
23247 Roo.bootstrap.SecurePass = function (config) {
23248     // these go here, so the translation tool can replace them..
23249     this.errors = {
23250         PwdEmpty: "Please type a password, and then retype it to confirm.",
23251         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23252         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23253         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23254         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23255         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23256         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23257         TooWeak: "Your password is Too Weak."
23258     },
23259     this.meterLabel = "Password strength:";
23260     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23261     this.meterClass = [
23262         "roo-password-meter-tooweak", 
23263         "roo-password-meter-weak", 
23264         "roo-password-meter-medium", 
23265         "roo-password-meter-strong", 
23266         "roo-password-meter-grey"
23267     ];
23268     
23269     this.errors = {};
23270     
23271     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23272 }
23273
23274 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23275     /**
23276      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23277      * {
23278      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23279      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23280      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23281      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23282      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23283      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23284      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23285      * })
23286      */
23287     // private
23288     
23289     meterWidth: 300,
23290     errorMsg :'',    
23291     errors: false,
23292     imageRoot: '/',
23293     /**
23294      * @cfg {String/Object} Label for the strength meter (defaults to
23295      * 'Password strength:')
23296      */
23297     // private
23298     meterLabel: '',
23299     /**
23300      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23301      * ['Weak', 'Medium', 'Strong'])
23302      */
23303     // private    
23304     pwdStrengths: false,    
23305     // private
23306     strength: 0,
23307     // private
23308     _lastPwd: null,
23309     // private
23310     kCapitalLetter: 0,
23311     kSmallLetter: 1,
23312     kDigit: 2,
23313     kPunctuation: 3,
23314     
23315     insecure: false,
23316     // private
23317     initEvents: function ()
23318     {
23319         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23320
23321         if (this.el.is('input[type=password]') && Roo.isSafari) {
23322             this.el.on('keydown', this.SafariOnKeyDown, this);
23323         }
23324
23325         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23326     },
23327     // private
23328     onRender: function (ct, position)
23329     {
23330         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23331         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23332         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23333
23334         this.trigger.createChild({
23335                    cn: [
23336                     {
23337                     //id: 'PwdMeter',
23338                     tag: 'div',
23339                     cls: 'roo-password-meter-grey col-xs-12',
23340                     style: {
23341                         //width: 0,
23342                         //width: this.meterWidth + 'px'                                                
23343                         }
23344                     },
23345                     {                            
23346                          cls: 'roo-password-meter-text'                          
23347                     }
23348                 ]            
23349         });
23350
23351          
23352         if (this.hideTrigger) {
23353             this.trigger.setDisplayed(false);
23354         }
23355         this.setSize(this.width || '', this.height || '');
23356     },
23357     // private
23358     onDestroy: function ()
23359     {
23360         if (this.trigger) {
23361             this.trigger.removeAllListeners();
23362             this.trigger.remove();
23363         }
23364         if (this.wrap) {
23365             this.wrap.remove();
23366         }
23367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23368     },
23369     // private
23370     checkStrength: function ()
23371     {
23372         var pwd = this.inputEl().getValue();
23373         if (pwd == this._lastPwd) {
23374             return;
23375         }
23376
23377         var strength;
23378         if (this.ClientSideStrongPassword(pwd)) {
23379             strength = 3;
23380         } else if (this.ClientSideMediumPassword(pwd)) {
23381             strength = 2;
23382         } else if (this.ClientSideWeakPassword(pwd)) {
23383             strength = 1;
23384         } else {
23385             strength = 0;
23386         }
23387         
23388         Roo.log('strength1: ' + strength);
23389         
23390         //var pm = this.trigger.child('div/div/div').dom;
23391         var pm = this.trigger.child('div/div');
23392         pm.removeClass(this.meterClass);
23393         pm.addClass(this.meterClass[strength]);
23394                 
23395         
23396         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23397                 
23398         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23399         
23400         this._lastPwd = pwd;
23401     },
23402     reset: function ()
23403     {
23404         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23405         
23406         this._lastPwd = '';
23407         
23408         var pm = this.trigger.child('div/div');
23409         pm.removeClass(this.meterClass);
23410         pm.addClass('roo-password-meter-grey');        
23411         
23412         
23413         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23414         
23415         pt.innerHTML = '';
23416         this.inputEl().dom.type='password';
23417     },
23418     // private
23419     validateValue: function (value)
23420     {
23421         
23422         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23423             return false;
23424         }
23425         if (value.length == 0) {
23426             if (this.allowBlank) {
23427                 this.clearInvalid();
23428                 return true;
23429             }
23430
23431             this.markInvalid(this.errors.PwdEmpty);
23432             this.errorMsg = this.errors.PwdEmpty;
23433             return false;
23434         }
23435         
23436         if(this.insecure){
23437             return true;
23438         }
23439         
23440         if ('[\x21-\x7e]*'.match(value)) {
23441             this.markInvalid(this.errors.PwdBadChar);
23442             this.errorMsg = this.errors.PwdBadChar;
23443             return false;
23444         }
23445         if (value.length < 6) {
23446             this.markInvalid(this.errors.PwdShort);
23447             this.errorMsg = this.errors.PwdShort;
23448             return false;
23449         }
23450         if (value.length > 16) {
23451             this.markInvalid(this.errors.PwdLong);
23452             this.errorMsg = this.errors.PwdLong;
23453             return false;
23454         }
23455         var strength;
23456         if (this.ClientSideStrongPassword(value)) {
23457             strength = 3;
23458         } else if (this.ClientSideMediumPassword(value)) {
23459             strength = 2;
23460         } else if (this.ClientSideWeakPassword(value)) {
23461             strength = 1;
23462         } else {
23463             strength = 0;
23464         }
23465
23466         
23467         if (strength < 2) {
23468             //this.markInvalid(this.errors.TooWeak);
23469             this.errorMsg = this.errors.TooWeak;
23470             //return false;
23471         }
23472         
23473         
23474         console.log('strength2: ' + strength);
23475         
23476         //var pm = this.trigger.child('div/div/div').dom;
23477         
23478         var pm = this.trigger.child('div/div');
23479         pm.removeClass(this.meterClass);
23480         pm.addClass(this.meterClass[strength]);
23481                 
23482         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23483                 
23484         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23485         
23486         this.errorMsg = ''; 
23487         return true;
23488     },
23489     // private
23490     CharacterSetChecks: function (type)
23491     {
23492         this.type = type;
23493         this.fResult = false;
23494     },
23495     // private
23496     isctype: function (character, type)
23497     {
23498         switch (type) {  
23499             case this.kCapitalLetter:
23500                 if (character >= 'A' && character <= 'Z') {
23501                     return true;
23502                 }
23503                 break;
23504             
23505             case this.kSmallLetter:
23506                 if (character >= 'a' && character <= 'z') {
23507                     return true;
23508                 }
23509                 break;
23510             
23511             case this.kDigit:
23512                 if (character >= '0' && character <= '9') {
23513                     return true;
23514                 }
23515                 break;
23516             
23517             case this.kPunctuation:
23518                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23519                     return true;
23520                 }
23521                 break;
23522             
23523             default:
23524                 return false;
23525         }
23526
23527     },
23528     // private
23529     IsLongEnough: function (pwd, size)
23530     {
23531         return !(pwd == null || isNaN(size) || pwd.length < size);
23532     },
23533     // private
23534     SpansEnoughCharacterSets: function (word, nb)
23535     {
23536         if (!this.IsLongEnough(word, nb))
23537         {
23538             return false;
23539         }
23540
23541         var characterSetChecks = new Array(
23542             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23543             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23544         );
23545         
23546         for (var index = 0; index < word.length; ++index) {
23547             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23548                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23549                     characterSetChecks[nCharSet].fResult = true;
23550                     break;
23551                 }
23552             }
23553         }
23554
23555         var nCharSets = 0;
23556         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23557             if (characterSetChecks[nCharSet].fResult) {
23558                 ++nCharSets;
23559             }
23560         }
23561
23562         if (nCharSets < nb) {
23563             return false;
23564         }
23565         return true;
23566     },
23567     // private
23568     ClientSideStrongPassword: function (pwd)
23569     {
23570         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23571     },
23572     // private
23573     ClientSideMediumPassword: function (pwd)
23574     {
23575         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23576     },
23577     // private
23578     ClientSideWeakPassword: function (pwd)
23579     {
23580         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23581     }
23582           
23583 })//<script type="text/javascript">
23584
23585 /*
23586  * Based  Ext JS Library 1.1.1
23587  * Copyright(c) 2006-2007, Ext JS, LLC.
23588  * LGPL
23589  *
23590  */
23591  
23592 /**
23593  * @class Roo.HtmlEditorCore
23594  * @extends Roo.Component
23595  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23596  *
23597  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23598  */
23599
23600 Roo.HtmlEditorCore = function(config){
23601     
23602     
23603     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23604     
23605     
23606     this.addEvents({
23607         /**
23608          * @event initialize
23609          * Fires when the editor is fully initialized (including the iframe)
23610          * @param {Roo.HtmlEditorCore} this
23611          */
23612         initialize: true,
23613         /**
23614          * @event activate
23615          * Fires when the editor is first receives the focus. Any insertion must wait
23616          * until after this event.
23617          * @param {Roo.HtmlEditorCore} this
23618          */
23619         activate: true,
23620          /**
23621          * @event beforesync
23622          * Fires before the textarea is updated with content from the editor iframe. Return false
23623          * to cancel the sync.
23624          * @param {Roo.HtmlEditorCore} this
23625          * @param {String} html
23626          */
23627         beforesync: true,
23628          /**
23629          * @event beforepush
23630          * Fires before the iframe editor is updated with content from the textarea. Return false
23631          * to cancel the push.
23632          * @param {Roo.HtmlEditorCore} this
23633          * @param {String} html
23634          */
23635         beforepush: true,
23636          /**
23637          * @event sync
23638          * Fires when the textarea is updated with content from the editor iframe.
23639          * @param {Roo.HtmlEditorCore} this
23640          * @param {String} html
23641          */
23642         sync: true,
23643          /**
23644          * @event push
23645          * Fires when the iframe editor is updated with content from the textarea.
23646          * @param {Roo.HtmlEditorCore} this
23647          * @param {String} html
23648          */
23649         push: true,
23650         
23651         /**
23652          * @event editorevent
23653          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23654          * @param {Roo.HtmlEditorCore} this
23655          */
23656         editorevent: true
23657         
23658     });
23659     
23660     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23661     
23662     // defaults : white / black...
23663     this.applyBlacklists();
23664     
23665     
23666     
23667 };
23668
23669
23670 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23671
23672
23673      /**
23674      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23675      */
23676     
23677     owner : false,
23678     
23679      /**
23680      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23681      *                        Roo.resizable.
23682      */
23683     resizable : false,
23684      /**
23685      * @cfg {Number} height (in pixels)
23686      */   
23687     height: 300,
23688    /**
23689      * @cfg {Number} width (in pixels)
23690      */   
23691     width: 500,
23692     
23693     /**
23694      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23695      * 
23696      */
23697     stylesheets: false,
23698     
23699     // id of frame..
23700     frameId: false,
23701     
23702     // private properties
23703     validationEvent : false,
23704     deferHeight: true,
23705     initialized : false,
23706     activated : false,
23707     sourceEditMode : false,
23708     onFocus : Roo.emptyFn,
23709     iframePad:3,
23710     hideMode:'offsets',
23711     
23712     clearUp: true,
23713     
23714     // blacklist + whitelisted elements..
23715     black: false,
23716     white: false,
23717      
23718     bodyCls : '',
23719
23720     /**
23721      * Protected method that will not generally be called directly. It
23722      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23723      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23724      */
23725     getDocMarkup : function(){
23726         // body styles..
23727         var st = '';
23728         
23729         // inherit styels from page...?? 
23730         if (this.stylesheets === false) {
23731             
23732             Roo.get(document.head).select('style').each(function(node) {
23733                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23734             });
23735             
23736             Roo.get(document.head).select('link').each(function(node) { 
23737                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23738             });
23739             
23740         } else if (!this.stylesheets.length) {
23741                 // simple..
23742                 st = '<style type="text/css">' +
23743                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23744                    '</style>';
23745         } else { 
23746             st = '<style type="text/css">' +
23747                     this.stylesheets +
23748                 '</style>';
23749         }
23750         
23751         st +=  '<style type="text/css">' +
23752             'IMG { cursor: pointer } ' +
23753         '</style>';
23754
23755         var cls = 'roo-htmleditor-body';
23756         
23757         if(this.bodyCls.length){
23758             cls += ' ' + this.bodyCls;
23759         }
23760         
23761         return '<html><head>' + st  +
23762             //<style type="text/css">' +
23763             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23764             //'</style>' +
23765             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23766     },
23767
23768     // private
23769     onRender : function(ct, position)
23770     {
23771         var _t = this;
23772         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23773         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23774         
23775         
23776         this.el.dom.style.border = '0 none';
23777         this.el.dom.setAttribute('tabIndex', -1);
23778         this.el.addClass('x-hidden hide');
23779         
23780         
23781         
23782         if(Roo.isIE){ // fix IE 1px bogus margin
23783             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23784         }
23785        
23786         
23787         this.frameId = Roo.id();
23788         
23789          
23790         
23791         var iframe = this.owner.wrap.createChild({
23792             tag: 'iframe',
23793             cls: 'form-control', // bootstrap..
23794             id: this.frameId,
23795             name: this.frameId,
23796             frameBorder : 'no',
23797             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23798         }, this.el
23799         );
23800         
23801         
23802         this.iframe = iframe.dom;
23803
23804          this.assignDocWin();
23805         
23806         this.doc.designMode = 'on';
23807        
23808         this.doc.open();
23809         this.doc.write(this.getDocMarkup());
23810         this.doc.close();
23811
23812         
23813         var task = { // must defer to wait for browser to be ready
23814             run : function(){
23815                 //console.log("run task?" + this.doc.readyState);
23816                 this.assignDocWin();
23817                 if(this.doc.body || this.doc.readyState == 'complete'){
23818                     try {
23819                         this.doc.designMode="on";
23820                     } catch (e) {
23821                         return;
23822                     }
23823                     Roo.TaskMgr.stop(task);
23824                     this.initEditor.defer(10, this);
23825                 }
23826             },
23827             interval : 10,
23828             duration: 10000,
23829             scope: this
23830         };
23831         Roo.TaskMgr.start(task);
23832
23833     },
23834
23835     // private
23836     onResize : function(w, h)
23837     {
23838          Roo.log('resize: ' +w + ',' + h );
23839         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23840         if(!this.iframe){
23841             return;
23842         }
23843         if(typeof w == 'number'){
23844             
23845             this.iframe.style.width = w + 'px';
23846         }
23847         if(typeof h == 'number'){
23848             
23849             this.iframe.style.height = h + 'px';
23850             if(this.doc){
23851                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23852             }
23853         }
23854         
23855     },
23856
23857     /**
23858      * Toggles the editor between standard and source edit mode.
23859      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23860      */
23861     toggleSourceEdit : function(sourceEditMode){
23862         
23863         this.sourceEditMode = sourceEditMode === true;
23864         
23865         if(this.sourceEditMode){
23866  
23867             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23868             
23869         }else{
23870             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23871             //this.iframe.className = '';
23872             this.deferFocus();
23873         }
23874         //this.setSize(this.owner.wrap.getSize());
23875         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23876     },
23877
23878     
23879   
23880
23881     /**
23882      * Protected method that will not generally be called directly. If you need/want
23883      * custom HTML cleanup, this is the method you should override.
23884      * @param {String} html The HTML to be cleaned
23885      * return {String} The cleaned HTML
23886      */
23887     cleanHtml : function(html){
23888         html = String(html);
23889         if(html.length > 5){
23890             if(Roo.isSafari){ // strip safari nonsense
23891                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23892             }
23893         }
23894         if(html == '&nbsp;'){
23895             html = '';
23896         }
23897         return html;
23898     },
23899
23900     /**
23901      * HTML Editor -> Textarea
23902      * Protected method that will not generally be called directly. Syncs the contents
23903      * of the editor iframe with the textarea.
23904      */
23905     syncValue : function(){
23906         if(this.initialized){
23907             var bd = (this.doc.body || this.doc.documentElement);
23908             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23909             var html = bd.innerHTML;
23910             if(Roo.isSafari){
23911                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23912                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23913                 if(m && m[1]){
23914                     html = '<div style="'+m[0]+'">' + html + '</div>';
23915                 }
23916             }
23917             html = this.cleanHtml(html);
23918             // fix up the special chars.. normaly like back quotes in word...
23919             // however we do not want to do this with chinese..
23920             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23921                 
23922                 var cc = match.charCodeAt();
23923
23924                 // Get the character value, handling surrogate pairs
23925                 if (match.length == 2) {
23926                     // It's a surrogate pair, calculate the Unicode code point
23927                     var high = match.charCodeAt(0) - 0xD800;
23928                     var low  = match.charCodeAt(1) - 0xDC00;
23929                     cc = (high * 0x400) + low + 0x10000;
23930                 }  else if (
23931                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23932                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23933                     (cc >= 0xf900 && cc < 0xfb00 )
23934                 ) {
23935                         return match;
23936                 }  
23937          
23938                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23939                 return "&#" + cc + ";";
23940                 
23941                 
23942             });
23943             
23944             
23945              
23946             if(this.owner.fireEvent('beforesync', this, html) !== false){
23947                 this.el.dom.value = html;
23948                 this.owner.fireEvent('sync', this, html);
23949             }
23950         }
23951     },
23952
23953     /**
23954      * Protected method that will not generally be called directly. Pushes the value of the textarea
23955      * into the iframe editor.
23956      */
23957     pushValue : function(){
23958         if(this.initialized){
23959             var v = this.el.dom.value.trim();
23960             
23961 //            if(v.length < 1){
23962 //                v = '&#160;';
23963 //            }
23964             
23965             if(this.owner.fireEvent('beforepush', this, v) !== false){
23966                 var d = (this.doc.body || this.doc.documentElement);
23967                 d.innerHTML = v;
23968                 this.cleanUpPaste();
23969                 this.el.dom.value = d.innerHTML;
23970                 this.owner.fireEvent('push', this, v);
23971             }
23972         }
23973     },
23974
23975     // private
23976     deferFocus : function(){
23977         this.focus.defer(10, this);
23978     },
23979
23980     // doc'ed in Field
23981     focus : function(){
23982         if(this.win && !this.sourceEditMode){
23983             this.win.focus();
23984         }else{
23985             this.el.focus();
23986         }
23987     },
23988     
23989     assignDocWin: function()
23990     {
23991         var iframe = this.iframe;
23992         
23993          if(Roo.isIE){
23994             this.doc = iframe.contentWindow.document;
23995             this.win = iframe.contentWindow;
23996         } else {
23997 //            if (!Roo.get(this.frameId)) {
23998 //                return;
23999 //            }
24000 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24001 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24002             
24003             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24004                 return;
24005             }
24006             
24007             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24008             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24009         }
24010     },
24011     
24012     // private
24013     initEditor : function(){
24014         //console.log("INIT EDITOR");
24015         this.assignDocWin();
24016         
24017         
24018         
24019         this.doc.designMode="on";
24020         this.doc.open();
24021         this.doc.write(this.getDocMarkup());
24022         this.doc.close();
24023         
24024         var dbody = (this.doc.body || this.doc.documentElement);
24025         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24026         // this copies styles from the containing element into thsi one..
24027         // not sure why we need all of this..
24028         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24029         
24030         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24031         //ss['background-attachment'] = 'fixed'; // w3c
24032         dbody.bgProperties = 'fixed'; // ie
24033         //Roo.DomHelper.applyStyles(dbody, ss);
24034         Roo.EventManager.on(this.doc, {
24035             //'mousedown': this.onEditorEvent,
24036             'mouseup': this.onEditorEvent,
24037             'dblclick': this.onEditorEvent,
24038             'click': this.onEditorEvent,
24039             'keyup': this.onEditorEvent,
24040             buffer:100,
24041             scope: this
24042         });
24043         if(Roo.isGecko){
24044             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24045         }
24046         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24047             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24048         }
24049         this.initialized = true;
24050
24051         this.owner.fireEvent('initialize', this);
24052         this.pushValue();
24053     },
24054
24055     // private
24056     onDestroy : function(){
24057         
24058         
24059         
24060         if(this.rendered){
24061             
24062             //for (var i =0; i < this.toolbars.length;i++) {
24063             //    // fixme - ask toolbars for heights?
24064             //    this.toolbars[i].onDestroy();
24065            // }
24066             
24067             //this.wrap.dom.innerHTML = '';
24068             //this.wrap.remove();
24069         }
24070     },
24071
24072     // private
24073     onFirstFocus : function(){
24074         
24075         this.assignDocWin();
24076         
24077         
24078         this.activated = true;
24079          
24080     
24081         if(Roo.isGecko){ // prevent silly gecko errors
24082             this.win.focus();
24083             var s = this.win.getSelection();
24084             if(!s.focusNode || s.focusNode.nodeType != 3){
24085                 var r = s.getRangeAt(0);
24086                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24087                 r.collapse(true);
24088                 this.deferFocus();
24089             }
24090             try{
24091                 this.execCmd('useCSS', true);
24092                 this.execCmd('styleWithCSS', false);
24093             }catch(e){}
24094         }
24095         this.owner.fireEvent('activate', this);
24096     },
24097
24098     // private
24099     adjustFont: function(btn){
24100         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24101         //if(Roo.isSafari){ // safari
24102         //    adjust *= 2;
24103        // }
24104         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24105         if(Roo.isSafari){ // safari
24106             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24107             v =  (v < 10) ? 10 : v;
24108             v =  (v > 48) ? 48 : v;
24109             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24110             
24111         }
24112         
24113         
24114         v = Math.max(1, v+adjust);
24115         
24116         this.execCmd('FontSize', v  );
24117     },
24118
24119     onEditorEvent : function(e)
24120     {
24121         this.owner.fireEvent('editorevent', this, e);
24122       //  this.updateToolbar();
24123         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24124     },
24125
24126     insertTag : function(tg)
24127     {
24128         // could be a bit smarter... -> wrap the current selected tRoo..
24129         if (tg.toLowerCase() == 'span' ||
24130             tg.toLowerCase() == 'code' ||
24131             tg.toLowerCase() == 'sup' ||
24132             tg.toLowerCase() == 'sub' 
24133             ) {
24134             
24135             range = this.createRange(this.getSelection());
24136             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24137             wrappingNode.appendChild(range.extractContents());
24138             range.insertNode(wrappingNode);
24139
24140             return;
24141             
24142             
24143             
24144         }
24145         this.execCmd("formatblock",   tg);
24146         
24147     },
24148     
24149     insertText : function(txt)
24150     {
24151         
24152         
24153         var range = this.createRange();
24154         range.deleteContents();
24155                //alert(Sender.getAttribute('label'));
24156                
24157         range.insertNode(this.doc.createTextNode(txt));
24158     } ,
24159     
24160      
24161
24162     /**
24163      * Executes a Midas editor command on the editor document and performs necessary focus and
24164      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24165      * @param {String} cmd The Midas command
24166      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24167      */
24168     relayCmd : function(cmd, value){
24169         this.win.focus();
24170         this.execCmd(cmd, value);
24171         this.owner.fireEvent('editorevent', this);
24172         //this.updateToolbar();
24173         this.owner.deferFocus();
24174     },
24175
24176     /**
24177      * Executes a Midas editor command directly on the editor document.
24178      * For visual commands, you should use {@link #relayCmd} instead.
24179      * <b>This should only be called after the editor is initialized.</b>
24180      * @param {String} cmd The Midas command
24181      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24182      */
24183     execCmd : function(cmd, value){
24184         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24185         this.syncValue();
24186     },
24187  
24188  
24189    
24190     /**
24191      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24192      * to insert tRoo.
24193      * @param {String} text | dom node.. 
24194      */
24195     insertAtCursor : function(text)
24196     {
24197         
24198         if(!this.activated){
24199             return;
24200         }
24201         /*
24202         if(Roo.isIE){
24203             this.win.focus();
24204             var r = this.doc.selection.createRange();
24205             if(r){
24206                 r.collapse(true);
24207                 r.pasteHTML(text);
24208                 this.syncValue();
24209                 this.deferFocus();
24210             
24211             }
24212             return;
24213         }
24214         */
24215         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24216             this.win.focus();
24217             
24218             
24219             // from jquery ui (MIT licenced)
24220             var range, node;
24221             var win = this.win;
24222             
24223             if (win.getSelection && win.getSelection().getRangeAt) {
24224                 range = win.getSelection().getRangeAt(0);
24225                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24226                 range.insertNode(node);
24227             } else if (win.document.selection && win.document.selection.createRange) {
24228                 // no firefox support
24229                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24230                 win.document.selection.createRange().pasteHTML(txt);
24231             } else {
24232                 // no firefox support
24233                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24234                 this.execCmd('InsertHTML', txt);
24235             } 
24236             
24237             this.syncValue();
24238             
24239             this.deferFocus();
24240         }
24241     },
24242  // private
24243     mozKeyPress : function(e){
24244         if(e.ctrlKey){
24245             var c = e.getCharCode(), cmd;
24246           
24247             if(c > 0){
24248                 c = String.fromCharCode(c).toLowerCase();
24249                 switch(c){
24250                     case 'b':
24251                         cmd = 'bold';
24252                         break;
24253                     case 'i':
24254                         cmd = 'italic';
24255                         break;
24256                     
24257                     case 'u':
24258                         cmd = 'underline';
24259                         break;
24260                     
24261                     case 'v':
24262                         this.cleanUpPaste.defer(100, this);
24263                         return;
24264                         
24265                 }
24266                 if(cmd){
24267                     this.win.focus();
24268                     this.execCmd(cmd);
24269                     this.deferFocus();
24270                     e.preventDefault();
24271                 }
24272                 
24273             }
24274         }
24275     },
24276
24277     // private
24278     fixKeys : function(){ // load time branching for fastest keydown performance
24279         if(Roo.isIE){
24280             return function(e){
24281                 var k = e.getKey(), r;
24282                 if(k == e.TAB){
24283                     e.stopEvent();
24284                     r = this.doc.selection.createRange();
24285                     if(r){
24286                         r.collapse(true);
24287                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24288                         this.deferFocus();
24289                     }
24290                     return;
24291                 }
24292                 
24293                 if(k == e.ENTER){
24294                     r = this.doc.selection.createRange();
24295                     if(r){
24296                         var target = r.parentElement();
24297                         if(!target || target.tagName.toLowerCase() != 'li'){
24298                             e.stopEvent();
24299                             r.pasteHTML('<br />');
24300                             r.collapse(false);
24301                             r.select();
24302                         }
24303                     }
24304                 }
24305                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24306                     this.cleanUpPaste.defer(100, this);
24307                     return;
24308                 }
24309                 
24310                 
24311             };
24312         }else if(Roo.isOpera){
24313             return function(e){
24314                 var k = e.getKey();
24315                 if(k == e.TAB){
24316                     e.stopEvent();
24317                     this.win.focus();
24318                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24319                     this.deferFocus();
24320                 }
24321                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24322                     this.cleanUpPaste.defer(100, this);
24323                     return;
24324                 }
24325                 
24326             };
24327         }else if(Roo.isSafari){
24328             return function(e){
24329                 var k = e.getKey();
24330                 
24331                 if(k == e.TAB){
24332                     e.stopEvent();
24333                     this.execCmd('InsertText','\t');
24334                     this.deferFocus();
24335                     return;
24336                 }
24337                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24338                     this.cleanUpPaste.defer(100, this);
24339                     return;
24340                 }
24341                 
24342              };
24343         }
24344     }(),
24345     
24346     getAllAncestors: function()
24347     {
24348         var p = this.getSelectedNode();
24349         var a = [];
24350         if (!p) {
24351             a.push(p); // push blank onto stack..
24352             p = this.getParentElement();
24353         }
24354         
24355         
24356         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24357             a.push(p);
24358             p = p.parentNode;
24359         }
24360         a.push(this.doc.body);
24361         return a;
24362     },
24363     lastSel : false,
24364     lastSelNode : false,
24365     
24366     
24367     getSelection : function() 
24368     {
24369         this.assignDocWin();
24370         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24371     },
24372     
24373     getSelectedNode: function() 
24374     {
24375         // this may only work on Gecko!!!
24376         
24377         // should we cache this!!!!
24378         
24379         
24380         
24381          
24382         var range = this.createRange(this.getSelection()).cloneRange();
24383         
24384         if (Roo.isIE) {
24385             var parent = range.parentElement();
24386             while (true) {
24387                 var testRange = range.duplicate();
24388                 testRange.moveToElementText(parent);
24389                 if (testRange.inRange(range)) {
24390                     break;
24391                 }
24392                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24393                     break;
24394                 }
24395                 parent = parent.parentElement;
24396             }
24397             return parent;
24398         }
24399         
24400         // is ancestor a text element.
24401         var ac =  range.commonAncestorContainer;
24402         if (ac.nodeType == 3) {
24403             ac = ac.parentNode;
24404         }
24405         
24406         var ar = ac.childNodes;
24407          
24408         var nodes = [];
24409         var other_nodes = [];
24410         var has_other_nodes = false;
24411         for (var i=0;i<ar.length;i++) {
24412             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24413                 continue;
24414             }
24415             // fullly contained node.
24416             
24417             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24418                 nodes.push(ar[i]);
24419                 continue;
24420             }
24421             
24422             // probably selected..
24423             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24424                 other_nodes.push(ar[i]);
24425                 continue;
24426             }
24427             // outer..
24428             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24429                 continue;
24430             }
24431             
24432             
24433             has_other_nodes = true;
24434         }
24435         if (!nodes.length && other_nodes.length) {
24436             nodes= other_nodes;
24437         }
24438         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24439             return false;
24440         }
24441         
24442         return nodes[0];
24443     },
24444     createRange: function(sel)
24445     {
24446         // this has strange effects when using with 
24447         // top toolbar - not sure if it's a great idea.
24448         //this.editor.contentWindow.focus();
24449         if (typeof sel != "undefined") {
24450             try {
24451                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24452             } catch(e) {
24453                 return this.doc.createRange();
24454             }
24455         } else {
24456             return this.doc.createRange();
24457         }
24458     },
24459     getParentElement: function()
24460     {
24461         
24462         this.assignDocWin();
24463         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24464         
24465         var range = this.createRange(sel);
24466          
24467         try {
24468             var p = range.commonAncestorContainer;
24469             while (p.nodeType == 3) { // text node
24470                 p = p.parentNode;
24471             }
24472             return p;
24473         } catch (e) {
24474             return null;
24475         }
24476     
24477     },
24478     /***
24479      *
24480      * Range intersection.. the hard stuff...
24481      *  '-1' = before
24482      *  '0' = hits..
24483      *  '1' = after.
24484      *         [ -- selected range --- ]
24485      *   [fail]                        [fail]
24486      *
24487      *    basically..
24488      *      if end is before start or  hits it. fail.
24489      *      if start is after end or hits it fail.
24490      *
24491      *   if either hits (but other is outside. - then it's not 
24492      *   
24493      *    
24494      **/
24495     
24496     
24497     // @see http://www.thismuchiknow.co.uk/?p=64.
24498     rangeIntersectsNode : function(range, node)
24499     {
24500         var nodeRange = node.ownerDocument.createRange();
24501         try {
24502             nodeRange.selectNode(node);
24503         } catch (e) {
24504             nodeRange.selectNodeContents(node);
24505         }
24506     
24507         var rangeStartRange = range.cloneRange();
24508         rangeStartRange.collapse(true);
24509     
24510         var rangeEndRange = range.cloneRange();
24511         rangeEndRange.collapse(false);
24512     
24513         var nodeStartRange = nodeRange.cloneRange();
24514         nodeStartRange.collapse(true);
24515     
24516         var nodeEndRange = nodeRange.cloneRange();
24517         nodeEndRange.collapse(false);
24518     
24519         return rangeStartRange.compareBoundaryPoints(
24520                  Range.START_TO_START, nodeEndRange) == -1 &&
24521                rangeEndRange.compareBoundaryPoints(
24522                  Range.START_TO_START, nodeStartRange) == 1;
24523         
24524          
24525     },
24526     rangeCompareNode : function(range, node)
24527     {
24528         var nodeRange = node.ownerDocument.createRange();
24529         try {
24530             nodeRange.selectNode(node);
24531         } catch (e) {
24532             nodeRange.selectNodeContents(node);
24533         }
24534         
24535         
24536         range.collapse(true);
24537     
24538         nodeRange.collapse(true);
24539      
24540         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24541         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24542          
24543         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24544         
24545         var nodeIsBefore   =  ss == 1;
24546         var nodeIsAfter    = ee == -1;
24547         
24548         if (nodeIsBefore && nodeIsAfter) {
24549             return 0; // outer
24550         }
24551         if (!nodeIsBefore && nodeIsAfter) {
24552             return 1; //right trailed.
24553         }
24554         
24555         if (nodeIsBefore && !nodeIsAfter) {
24556             return 2;  // left trailed.
24557         }
24558         // fully contined.
24559         return 3;
24560     },
24561
24562     // private? - in a new class?
24563     cleanUpPaste :  function()
24564     {
24565         // cleans up the whole document..
24566         Roo.log('cleanuppaste');
24567         
24568         this.cleanUpChildren(this.doc.body);
24569         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24570         if (clean != this.doc.body.innerHTML) {
24571             this.doc.body.innerHTML = clean;
24572         }
24573         
24574     },
24575     
24576     cleanWordChars : function(input) {// change the chars to hex code
24577         var he = Roo.HtmlEditorCore;
24578         
24579         var output = input;
24580         Roo.each(he.swapCodes, function(sw) { 
24581             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24582             
24583             output = output.replace(swapper, sw[1]);
24584         });
24585         
24586         return output;
24587     },
24588     
24589     
24590     cleanUpChildren : function (n)
24591     {
24592         if (!n.childNodes.length) {
24593             return;
24594         }
24595         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24596            this.cleanUpChild(n.childNodes[i]);
24597         }
24598     },
24599     
24600     
24601         
24602     
24603     cleanUpChild : function (node)
24604     {
24605         var ed = this;
24606         //console.log(node);
24607         if (node.nodeName == "#text") {
24608             // clean up silly Windows -- stuff?
24609             return; 
24610         }
24611         if (node.nodeName == "#comment") {
24612             node.parentNode.removeChild(node);
24613             // clean up silly Windows -- stuff?
24614             return; 
24615         }
24616         var lcname = node.tagName.toLowerCase();
24617         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24618         // whitelist of tags..
24619         
24620         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24621             // remove node.
24622             node.parentNode.removeChild(node);
24623             return;
24624             
24625         }
24626         
24627         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24628         
24629         // spans with no attributes - just remove them..
24630         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24631             remove_keep_children = true;
24632         }
24633         
24634         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24635         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24636         
24637         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24638         //    remove_keep_children = true;
24639         //}
24640         
24641         if (remove_keep_children) {
24642             this.cleanUpChildren(node);
24643             // inserts everything just before this node...
24644             while (node.childNodes.length) {
24645                 var cn = node.childNodes[0];
24646                 node.removeChild(cn);
24647                 node.parentNode.insertBefore(cn, node);
24648             }
24649             node.parentNode.removeChild(node);
24650             return;
24651         }
24652         
24653         if (!node.attributes || !node.attributes.length) {
24654             
24655           
24656             
24657             
24658             this.cleanUpChildren(node);
24659             return;
24660         }
24661         
24662         function cleanAttr(n,v)
24663         {
24664             
24665             if (v.match(/^\./) || v.match(/^\//)) {
24666                 return;
24667             }
24668             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24669                 return;
24670             }
24671             if (v.match(/^#/)) {
24672                 return;
24673             }
24674 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24675             node.removeAttribute(n);
24676             
24677         }
24678         
24679         var cwhite = this.cwhite;
24680         var cblack = this.cblack;
24681             
24682         function cleanStyle(n,v)
24683         {
24684             if (v.match(/expression/)) { //XSS?? should we even bother..
24685                 node.removeAttribute(n);
24686                 return;
24687             }
24688             
24689             var parts = v.split(/;/);
24690             var clean = [];
24691             
24692             Roo.each(parts, function(p) {
24693                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24694                 if (!p.length) {
24695                     return true;
24696                 }
24697                 var l = p.split(':').shift().replace(/\s+/g,'');
24698                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24699                 
24700                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24701 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24702                     //node.removeAttribute(n);
24703                     return true;
24704                 }
24705                 //Roo.log()
24706                 // only allow 'c whitelisted system attributes'
24707                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24708 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24709                     //node.removeAttribute(n);
24710                     return true;
24711                 }
24712                 
24713                 
24714                  
24715                 
24716                 clean.push(p);
24717                 return true;
24718             });
24719             if (clean.length) { 
24720                 node.setAttribute(n, clean.join(';'));
24721             } else {
24722                 node.removeAttribute(n);
24723             }
24724             
24725         }
24726         
24727         
24728         for (var i = node.attributes.length-1; i > -1 ; i--) {
24729             var a = node.attributes[i];
24730             //console.log(a);
24731             
24732             if (a.name.toLowerCase().substr(0,2)=='on')  {
24733                 node.removeAttribute(a.name);
24734                 continue;
24735             }
24736             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24737                 node.removeAttribute(a.name);
24738                 continue;
24739             }
24740             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24741                 cleanAttr(a.name,a.value); // fixme..
24742                 continue;
24743             }
24744             if (a.name == 'style') {
24745                 cleanStyle(a.name,a.value);
24746                 continue;
24747             }
24748             /// clean up MS crap..
24749             // tecnically this should be a list of valid class'es..
24750             
24751             
24752             if (a.name == 'class') {
24753                 if (a.value.match(/^Mso/)) {
24754                     node.removeAttribute('class');
24755                 }
24756                 
24757                 if (a.value.match(/^body$/)) {
24758                     node.removeAttribute('class');
24759                 }
24760                 continue;
24761             }
24762             
24763             // style cleanup!?
24764             // class cleanup?
24765             
24766         }
24767         
24768         
24769         this.cleanUpChildren(node);
24770         
24771         
24772     },
24773     
24774     /**
24775      * Clean up MS wordisms...
24776      */
24777     cleanWord : function(node)
24778     {
24779         if (!node) {
24780             this.cleanWord(this.doc.body);
24781             return;
24782         }
24783         
24784         if(
24785                 node.nodeName == 'SPAN' &&
24786                 !node.hasAttributes() &&
24787                 node.childNodes.length == 1 &&
24788                 node.firstChild.nodeName == "#text"  
24789         ) {
24790             var textNode = node.firstChild;
24791             node.removeChild(textNode);
24792             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24793                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24794             }
24795             node.parentNode.insertBefore(textNode, node);
24796             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24797                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24798             }
24799             node.parentNode.removeChild(node);
24800         }
24801         
24802         if (node.nodeName == "#text") {
24803             // clean up silly Windows -- stuff?
24804             return; 
24805         }
24806         if (node.nodeName == "#comment") {
24807             node.parentNode.removeChild(node);
24808             // clean up silly Windows -- stuff?
24809             return; 
24810         }
24811         
24812         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24813             node.parentNode.removeChild(node);
24814             return;
24815         }
24816         //Roo.log(node.tagName);
24817         // remove - but keep children..
24818         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24819             //Roo.log('-- removed');
24820             while (node.childNodes.length) {
24821                 var cn = node.childNodes[0];
24822                 node.removeChild(cn);
24823                 node.parentNode.insertBefore(cn, node);
24824                 // move node to parent - and clean it..
24825                 this.cleanWord(cn);
24826             }
24827             node.parentNode.removeChild(node);
24828             /// no need to iterate chidlren = it's got none..
24829             //this.iterateChildren(node, this.cleanWord);
24830             return;
24831         }
24832         // clean styles
24833         if (node.className.length) {
24834             
24835             var cn = node.className.split(/\W+/);
24836             var cna = [];
24837             Roo.each(cn, function(cls) {
24838                 if (cls.match(/Mso[a-zA-Z]+/)) {
24839                     return;
24840                 }
24841                 cna.push(cls);
24842             });
24843             node.className = cna.length ? cna.join(' ') : '';
24844             if (!cna.length) {
24845                 node.removeAttribute("class");
24846             }
24847         }
24848         
24849         if (node.hasAttribute("lang")) {
24850             node.removeAttribute("lang");
24851         }
24852         
24853         if (node.hasAttribute("style")) {
24854             
24855             var styles = node.getAttribute("style").split(";");
24856             var nstyle = [];
24857             Roo.each(styles, function(s) {
24858                 if (!s.match(/:/)) {
24859                     return;
24860                 }
24861                 var kv = s.split(":");
24862                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24863                     return;
24864                 }
24865                 // what ever is left... we allow.
24866                 nstyle.push(s);
24867             });
24868             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24869             if (!nstyle.length) {
24870                 node.removeAttribute('style');
24871             }
24872         }
24873         this.iterateChildren(node, this.cleanWord);
24874         
24875         
24876         
24877     },
24878     /**
24879      * iterateChildren of a Node, calling fn each time, using this as the scole..
24880      * @param {DomNode} node node to iterate children of.
24881      * @param {Function} fn method of this class to call on each item.
24882      */
24883     iterateChildren : function(node, fn)
24884     {
24885         if (!node.childNodes.length) {
24886                 return;
24887         }
24888         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24889            fn.call(this, node.childNodes[i])
24890         }
24891     },
24892     
24893     
24894     /**
24895      * cleanTableWidths.
24896      *
24897      * Quite often pasting from word etc.. results in tables with column and widths.
24898      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24899      *
24900      */
24901     cleanTableWidths : function(node)
24902     {
24903          
24904          
24905         if (!node) {
24906             this.cleanTableWidths(this.doc.body);
24907             return;
24908         }
24909         
24910         // ignore list...
24911         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24912             return; 
24913         }
24914         Roo.log(node.tagName);
24915         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24916             this.iterateChildren(node, this.cleanTableWidths);
24917             return;
24918         }
24919         if (node.hasAttribute('width')) {
24920             node.removeAttribute('width');
24921         }
24922         
24923          
24924         if (node.hasAttribute("style")) {
24925             // pretty basic...
24926             
24927             var styles = node.getAttribute("style").split(";");
24928             var nstyle = [];
24929             Roo.each(styles, function(s) {
24930                 if (!s.match(/:/)) {
24931                     return;
24932                 }
24933                 var kv = s.split(":");
24934                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24935                     return;
24936                 }
24937                 // what ever is left... we allow.
24938                 nstyle.push(s);
24939             });
24940             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24941             if (!nstyle.length) {
24942                 node.removeAttribute('style');
24943             }
24944         }
24945         
24946         this.iterateChildren(node, this.cleanTableWidths);
24947         
24948         
24949     },
24950     
24951     
24952     
24953     
24954     domToHTML : function(currentElement, depth, nopadtext) {
24955         
24956         depth = depth || 0;
24957         nopadtext = nopadtext || false;
24958     
24959         if (!currentElement) {
24960             return this.domToHTML(this.doc.body);
24961         }
24962         
24963         //Roo.log(currentElement);
24964         var j;
24965         var allText = false;
24966         var nodeName = currentElement.nodeName;
24967         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24968         
24969         if  (nodeName == '#text') {
24970             
24971             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24972         }
24973         
24974         
24975         var ret = '';
24976         if (nodeName != 'BODY') {
24977              
24978             var i = 0;
24979             // Prints the node tagName, such as <A>, <IMG>, etc
24980             if (tagName) {
24981                 var attr = [];
24982                 for(i = 0; i < currentElement.attributes.length;i++) {
24983                     // quoting?
24984                     var aname = currentElement.attributes.item(i).name;
24985                     if (!currentElement.attributes.item(i).value.length) {
24986                         continue;
24987                     }
24988                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
24989                 }
24990                 
24991                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
24992             } 
24993             else {
24994                 
24995                 // eack
24996             }
24997         } else {
24998             tagName = false;
24999         }
25000         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25001             return ret;
25002         }
25003         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25004             nopadtext = true;
25005         }
25006         
25007         
25008         // Traverse the tree
25009         i = 0;
25010         var currentElementChild = currentElement.childNodes.item(i);
25011         var allText = true;
25012         var innerHTML  = '';
25013         lastnode = '';
25014         while (currentElementChild) {
25015             // Formatting code (indent the tree so it looks nice on the screen)
25016             var nopad = nopadtext;
25017             if (lastnode == 'SPAN') {
25018                 nopad  = true;
25019             }
25020             // text
25021             if  (currentElementChild.nodeName == '#text') {
25022                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25023                 toadd = nopadtext ? toadd : toadd.trim();
25024                 if (!nopad && toadd.length > 80) {
25025                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25026                 }
25027                 innerHTML  += toadd;
25028                 
25029                 i++;
25030                 currentElementChild = currentElement.childNodes.item(i);
25031                 lastNode = '';
25032                 continue;
25033             }
25034             allText = false;
25035             
25036             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25037                 
25038             // Recursively traverse the tree structure of the child node
25039             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25040             lastnode = currentElementChild.nodeName;
25041             i++;
25042             currentElementChild=currentElement.childNodes.item(i);
25043         }
25044         
25045         ret += innerHTML;
25046         
25047         if (!allText) {
25048                 // The remaining code is mostly for formatting the tree
25049             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25050         }
25051         
25052         
25053         if (tagName) {
25054             ret+= "</"+tagName+">";
25055         }
25056         return ret;
25057         
25058     },
25059         
25060     applyBlacklists : function()
25061     {
25062         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25063         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25064         
25065         this.white = [];
25066         this.black = [];
25067         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25068             if (b.indexOf(tag) > -1) {
25069                 return;
25070             }
25071             this.white.push(tag);
25072             
25073         }, this);
25074         
25075         Roo.each(w, function(tag) {
25076             if (b.indexOf(tag) > -1) {
25077                 return;
25078             }
25079             if (this.white.indexOf(tag) > -1) {
25080                 return;
25081             }
25082             this.white.push(tag);
25083             
25084         }, this);
25085         
25086         
25087         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25088             if (w.indexOf(tag) > -1) {
25089                 return;
25090             }
25091             this.black.push(tag);
25092             
25093         }, this);
25094         
25095         Roo.each(b, function(tag) {
25096             if (w.indexOf(tag) > -1) {
25097                 return;
25098             }
25099             if (this.black.indexOf(tag) > -1) {
25100                 return;
25101             }
25102             this.black.push(tag);
25103             
25104         }, this);
25105         
25106         
25107         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25108         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25109         
25110         this.cwhite = [];
25111         this.cblack = [];
25112         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25113             if (b.indexOf(tag) > -1) {
25114                 return;
25115             }
25116             this.cwhite.push(tag);
25117             
25118         }, this);
25119         
25120         Roo.each(w, function(tag) {
25121             if (b.indexOf(tag) > -1) {
25122                 return;
25123             }
25124             if (this.cwhite.indexOf(tag) > -1) {
25125                 return;
25126             }
25127             this.cwhite.push(tag);
25128             
25129         }, this);
25130         
25131         
25132         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25133             if (w.indexOf(tag) > -1) {
25134                 return;
25135             }
25136             this.cblack.push(tag);
25137             
25138         }, this);
25139         
25140         Roo.each(b, function(tag) {
25141             if (w.indexOf(tag) > -1) {
25142                 return;
25143             }
25144             if (this.cblack.indexOf(tag) > -1) {
25145                 return;
25146             }
25147             this.cblack.push(tag);
25148             
25149         }, this);
25150     },
25151     
25152     setStylesheets : function(stylesheets)
25153     {
25154         if(typeof(stylesheets) == 'string'){
25155             Roo.get(this.iframe.contentDocument.head).createChild({
25156                 tag : 'link',
25157                 rel : 'stylesheet',
25158                 type : 'text/css',
25159                 href : stylesheets
25160             });
25161             
25162             return;
25163         }
25164         var _this = this;
25165      
25166         Roo.each(stylesheets, function(s) {
25167             if(!s.length){
25168                 return;
25169             }
25170             
25171             Roo.get(_this.iframe.contentDocument.head).createChild({
25172                 tag : 'link',
25173                 rel : 'stylesheet',
25174                 type : 'text/css',
25175                 href : s
25176             });
25177         });
25178
25179         
25180     },
25181     
25182     removeStylesheets : function()
25183     {
25184         var _this = this;
25185         
25186         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25187             s.remove();
25188         });
25189     },
25190     
25191     setStyle : function(style)
25192     {
25193         Roo.get(this.iframe.contentDocument.head).createChild({
25194             tag : 'style',
25195             type : 'text/css',
25196             html : style
25197         });
25198
25199         return;
25200     }
25201     
25202     // hide stuff that is not compatible
25203     /**
25204      * @event blur
25205      * @hide
25206      */
25207     /**
25208      * @event change
25209      * @hide
25210      */
25211     /**
25212      * @event focus
25213      * @hide
25214      */
25215     /**
25216      * @event specialkey
25217      * @hide
25218      */
25219     /**
25220      * @cfg {String} fieldClass @hide
25221      */
25222     /**
25223      * @cfg {String} focusClass @hide
25224      */
25225     /**
25226      * @cfg {String} autoCreate @hide
25227      */
25228     /**
25229      * @cfg {String} inputType @hide
25230      */
25231     /**
25232      * @cfg {String} invalidClass @hide
25233      */
25234     /**
25235      * @cfg {String} invalidText @hide
25236      */
25237     /**
25238      * @cfg {String} msgFx @hide
25239      */
25240     /**
25241      * @cfg {String} validateOnBlur @hide
25242      */
25243 });
25244
25245 Roo.HtmlEditorCore.white = [
25246         'area', 'br', 'img', 'input', 'hr', 'wbr',
25247         
25248        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25249        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25250        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25251        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25252        'table',   'ul',         'xmp', 
25253        
25254        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25255       'thead',   'tr', 
25256      
25257       'dir', 'menu', 'ol', 'ul', 'dl',
25258        
25259       'embed',  'object'
25260 ];
25261
25262
25263 Roo.HtmlEditorCore.black = [
25264     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25265         'applet', // 
25266         'base',   'basefont', 'bgsound', 'blink',  'body', 
25267         'frame',  'frameset', 'head',    'html',   'ilayer', 
25268         'iframe', 'layer',  'link',     'meta',    'object',   
25269         'script', 'style' ,'title',  'xml' // clean later..
25270 ];
25271 Roo.HtmlEditorCore.clean = [
25272     'script', 'style', 'title', 'xml'
25273 ];
25274 Roo.HtmlEditorCore.remove = [
25275     'font'
25276 ];
25277 // attributes..
25278
25279 Roo.HtmlEditorCore.ablack = [
25280     'on'
25281 ];
25282     
25283 Roo.HtmlEditorCore.aclean = [ 
25284     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25285 ];
25286
25287 // protocols..
25288 Roo.HtmlEditorCore.pwhite= [
25289         'http',  'https',  'mailto'
25290 ];
25291
25292 // white listed style attributes.
25293 Roo.HtmlEditorCore.cwhite= [
25294       //  'text-align', /// default is to allow most things..
25295       
25296          
25297 //        'font-size'//??
25298 ];
25299
25300 // black listed style attributes.
25301 Roo.HtmlEditorCore.cblack= [
25302       //  'font-size' -- this can be set by the project 
25303 ];
25304
25305
25306 Roo.HtmlEditorCore.swapCodes   =[ 
25307     [    8211, "--" ], 
25308     [    8212, "--" ], 
25309     [    8216,  "'" ],  
25310     [    8217, "'" ],  
25311     [    8220, '"' ],  
25312     [    8221, '"' ],  
25313     [    8226, "*" ],  
25314     [    8230, "..." ]
25315 ]; 
25316
25317     /*
25318  * - LGPL
25319  *
25320  * HtmlEditor
25321  * 
25322  */
25323
25324 /**
25325  * @class Roo.bootstrap.HtmlEditor
25326  * @extends Roo.bootstrap.TextArea
25327  * Bootstrap HtmlEditor class
25328
25329  * @constructor
25330  * Create a new HtmlEditor
25331  * @param {Object} config The config object
25332  */
25333
25334 Roo.bootstrap.HtmlEditor = function(config){
25335     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25336     if (!this.toolbars) {
25337         this.toolbars = [];
25338     }
25339     
25340     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25341     this.addEvents({
25342             /**
25343              * @event initialize
25344              * Fires when the editor is fully initialized (including the iframe)
25345              * @param {HtmlEditor} this
25346              */
25347             initialize: true,
25348             /**
25349              * @event activate
25350              * Fires when the editor is first receives the focus. Any insertion must wait
25351              * until after this event.
25352              * @param {HtmlEditor} this
25353              */
25354             activate: true,
25355              /**
25356              * @event beforesync
25357              * Fires before the textarea is updated with content from the editor iframe. Return false
25358              * to cancel the sync.
25359              * @param {HtmlEditor} this
25360              * @param {String} html
25361              */
25362             beforesync: true,
25363              /**
25364              * @event beforepush
25365              * Fires before the iframe editor is updated with content from the textarea. Return false
25366              * to cancel the push.
25367              * @param {HtmlEditor} this
25368              * @param {String} html
25369              */
25370             beforepush: true,
25371              /**
25372              * @event sync
25373              * Fires when the textarea is updated with content from the editor iframe.
25374              * @param {HtmlEditor} this
25375              * @param {String} html
25376              */
25377             sync: true,
25378              /**
25379              * @event push
25380              * Fires when the iframe editor is updated with content from the textarea.
25381              * @param {HtmlEditor} this
25382              * @param {String} html
25383              */
25384             push: true,
25385              /**
25386              * @event editmodechange
25387              * Fires when the editor switches edit modes
25388              * @param {HtmlEditor} this
25389              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25390              */
25391             editmodechange: true,
25392             /**
25393              * @event editorevent
25394              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25395              * @param {HtmlEditor} this
25396              */
25397             editorevent: true,
25398             /**
25399              * @event firstfocus
25400              * Fires when on first focus - needed by toolbars..
25401              * @param {HtmlEditor} this
25402              */
25403             firstfocus: true,
25404             /**
25405              * @event autosave
25406              * Auto save the htmlEditor value as a file into Events
25407              * @param {HtmlEditor} this
25408              */
25409             autosave: true,
25410             /**
25411              * @event savedpreview
25412              * preview the saved version of htmlEditor
25413              * @param {HtmlEditor} this
25414              */
25415             savedpreview: true
25416         });
25417 };
25418
25419
25420 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25421     
25422     
25423       /**
25424      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25425      */
25426     toolbars : false,
25427     
25428      /**
25429     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25430     */
25431     btns : [],
25432    
25433      /**
25434      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25435      *                        Roo.resizable.
25436      */
25437     resizable : false,
25438      /**
25439      * @cfg {Number} height (in pixels)
25440      */   
25441     height: 300,
25442    /**
25443      * @cfg {Number} width (in pixels)
25444      */   
25445     width: false,
25446     
25447     /**
25448      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25449      * 
25450      */
25451     stylesheets: false,
25452     
25453     // id of frame..
25454     frameId: false,
25455     
25456     // private properties
25457     validationEvent : false,
25458     deferHeight: true,
25459     initialized : false,
25460     activated : false,
25461     
25462     onFocus : Roo.emptyFn,
25463     iframePad:3,
25464     hideMode:'offsets',
25465     
25466     tbContainer : false,
25467     
25468     bodyCls : '',
25469     
25470     toolbarContainer :function() {
25471         return this.wrap.select('.x-html-editor-tb',true).first();
25472     },
25473
25474     /**
25475      * Protected method that will not generally be called directly. It
25476      * is called when the editor creates its toolbar. Override this method if you need to
25477      * add custom toolbar buttons.
25478      * @param {HtmlEditor} editor
25479      */
25480     createToolbar : function(){
25481         Roo.log('renewing');
25482         Roo.log("create toolbars");
25483         
25484         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25485         this.toolbars[0].render(this.toolbarContainer());
25486         
25487         return;
25488         
25489 //        if (!editor.toolbars || !editor.toolbars.length) {
25490 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25491 //        }
25492 //        
25493 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25494 //            editor.toolbars[i] = Roo.factory(
25495 //                    typeof(editor.toolbars[i]) == 'string' ?
25496 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25497 //                Roo.bootstrap.HtmlEditor);
25498 //            editor.toolbars[i].init(editor);
25499 //        }
25500     },
25501
25502      
25503     // private
25504     onRender : function(ct, position)
25505     {
25506        // Roo.log("Call onRender: " + this.xtype);
25507         var _t = this;
25508         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25509       
25510         this.wrap = this.inputEl().wrap({
25511             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25512         });
25513         
25514         this.editorcore.onRender(ct, position);
25515          
25516         if (this.resizable) {
25517             this.resizeEl = new Roo.Resizable(this.wrap, {
25518                 pinned : true,
25519                 wrap: true,
25520                 dynamic : true,
25521                 minHeight : this.height,
25522                 height: this.height,
25523                 handles : this.resizable,
25524                 width: this.width,
25525                 listeners : {
25526                     resize : function(r, w, h) {
25527                         _t.onResize(w,h); // -something
25528                     }
25529                 }
25530             });
25531             
25532         }
25533         this.createToolbar(this);
25534        
25535         
25536         if(!this.width && this.resizable){
25537             this.setSize(this.wrap.getSize());
25538         }
25539         if (this.resizeEl) {
25540             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25541             // should trigger onReize..
25542         }
25543         
25544     },
25545
25546     // private
25547     onResize : function(w, h)
25548     {
25549         Roo.log('resize: ' +w + ',' + h );
25550         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25551         var ew = false;
25552         var eh = false;
25553         
25554         if(this.inputEl() ){
25555             if(typeof w == 'number'){
25556                 var aw = w - this.wrap.getFrameWidth('lr');
25557                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25558                 ew = aw;
25559             }
25560             if(typeof h == 'number'){
25561                  var tbh = -11;  // fixme it needs to tool bar size!
25562                 for (var i =0; i < this.toolbars.length;i++) {
25563                     // fixme - ask toolbars for heights?
25564                     tbh += this.toolbars[i].el.getHeight();
25565                     //if (this.toolbars[i].footer) {
25566                     //    tbh += this.toolbars[i].footer.el.getHeight();
25567                     //}
25568                 }
25569               
25570                 
25571                 
25572                 
25573                 
25574                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25575                 ah -= 5; // knock a few pixes off for look..
25576                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25577                 var eh = ah;
25578             }
25579         }
25580         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25581         this.editorcore.onResize(ew,eh);
25582         
25583     },
25584
25585     /**
25586      * Toggles the editor between standard and source edit mode.
25587      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25588      */
25589     toggleSourceEdit : function(sourceEditMode)
25590     {
25591         this.editorcore.toggleSourceEdit(sourceEditMode);
25592         
25593         if(this.editorcore.sourceEditMode){
25594             Roo.log('editor - showing textarea');
25595             
25596 //            Roo.log('in');
25597 //            Roo.log(this.syncValue());
25598             this.syncValue();
25599             this.inputEl().removeClass(['hide', 'x-hidden']);
25600             this.inputEl().dom.removeAttribute('tabIndex');
25601             this.inputEl().focus();
25602         }else{
25603             Roo.log('editor - hiding textarea');
25604 //            Roo.log('out')
25605 //            Roo.log(this.pushValue()); 
25606             this.pushValue();
25607             
25608             this.inputEl().addClass(['hide', 'x-hidden']);
25609             this.inputEl().dom.setAttribute('tabIndex', -1);
25610             //this.deferFocus();
25611         }
25612          
25613         if(this.resizable){
25614             this.setSize(this.wrap.getSize());
25615         }
25616         
25617         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25618     },
25619  
25620     // private (for BoxComponent)
25621     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25622
25623     // private (for BoxComponent)
25624     getResizeEl : function(){
25625         return this.wrap;
25626     },
25627
25628     // private (for BoxComponent)
25629     getPositionEl : function(){
25630         return this.wrap;
25631     },
25632
25633     // private
25634     initEvents : function(){
25635         this.originalValue = this.getValue();
25636     },
25637
25638 //    /**
25639 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25640 //     * @method
25641 //     */
25642 //    markInvalid : Roo.emptyFn,
25643 //    /**
25644 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25645 //     * @method
25646 //     */
25647 //    clearInvalid : Roo.emptyFn,
25648
25649     setValue : function(v){
25650         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25651         this.editorcore.pushValue();
25652     },
25653
25654      
25655     // private
25656     deferFocus : function(){
25657         this.focus.defer(10, this);
25658     },
25659
25660     // doc'ed in Field
25661     focus : function(){
25662         this.editorcore.focus();
25663         
25664     },
25665       
25666
25667     // private
25668     onDestroy : function(){
25669         
25670         
25671         
25672         if(this.rendered){
25673             
25674             for (var i =0; i < this.toolbars.length;i++) {
25675                 // fixme - ask toolbars for heights?
25676                 this.toolbars[i].onDestroy();
25677             }
25678             
25679             this.wrap.dom.innerHTML = '';
25680             this.wrap.remove();
25681         }
25682     },
25683
25684     // private
25685     onFirstFocus : function(){
25686         //Roo.log("onFirstFocus");
25687         this.editorcore.onFirstFocus();
25688          for (var i =0; i < this.toolbars.length;i++) {
25689             this.toolbars[i].onFirstFocus();
25690         }
25691         
25692     },
25693     
25694     // private
25695     syncValue : function()
25696     {   
25697         this.editorcore.syncValue();
25698     },
25699     
25700     pushValue : function()
25701     {   
25702         this.editorcore.pushValue();
25703     }
25704      
25705     
25706     // hide stuff that is not compatible
25707     /**
25708      * @event blur
25709      * @hide
25710      */
25711     /**
25712      * @event change
25713      * @hide
25714      */
25715     /**
25716      * @event focus
25717      * @hide
25718      */
25719     /**
25720      * @event specialkey
25721      * @hide
25722      */
25723     /**
25724      * @cfg {String} fieldClass @hide
25725      */
25726     /**
25727      * @cfg {String} focusClass @hide
25728      */
25729     /**
25730      * @cfg {String} autoCreate @hide
25731      */
25732     /**
25733      * @cfg {String} inputType @hide
25734      */
25735      
25736     /**
25737      * @cfg {String} invalidText @hide
25738      */
25739     /**
25740      * @cfg {String} msgFx @hide
25741      */
25742     /**
25743      * @cfg {String} validateOnBlur @hide
25744      */
25745 });
25746  
25747     
25748    
25749    
25750    
25751       
25752 Roo.namespace('Roo.bootstrap.htmleditor');
25753 /**
25754  * @class Roo.bootstrap.HtmlEditorToolbar1
25755  * Basic Toolbar
25756  * 
25757  * @example
25758  * Usage:
25759  *
25760  new Roo.bootstrap.HtmlEditor({
25761     ....
25762     toolbars : [
25763         new Roo.bootstrap.HtmlEditorToolbar1({
25764             disable : { fonts: 1 , format: 1, ..., ... , ...],
25765             btns : [ .... ]
25766         })
25767     }
25768      
25769  * 
25770  * @cfg {Object} disable List of elements to disable..
25771  * @cfg {Array} btns List of additional buttons.
25772  * 
25773  * 
25774  * NEEDS Extra CSS? 
25775  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25776  */
25777  
25778 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25779 {
25780     
25781     Roo.apply(this, config);
25782     
25783     // default disabled, based on 'good practice'..
25784     this.disable = this.disable || {};
25785     Roo.applyIf(this.disable, {
25786         fontSize : true,
25787         colors : true,
25788         specialElements : true
25789     });
25790     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25791     
25792     this.editor = config.editor;
25793     this.editorcore = config.editor.editorcore;
25794     
25795     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25796     
25797     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25798     // dont call parent... till later.
25799 }
25800 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25801      
25802     bar : true,
25803     
25804     editor : false,
25805     editorcore : false,
25806     
25807     
25808     formats : [
25809         "p" ,  
25810         "h1","h2","h3","h4","h5","h6", 
25811         "pre", "code", 
25812         "abbr", "acronym", "address", "cite", "samp", "var",
25813         'div','span'
25814     ],
25815     
25816     onRender : function(ct, position)
25817     {
25818        // Roo.log("Call onRender: " + this.xtype);
25819         
25820        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25821        Roo.log(this.el);
25822        this.el.dom.style.marginBottom = '0';
25823        var _this = this;
25824        var editorcore = this.editorcore;
25825        var editor= this.editor;
25826        
25827        var children = [];
25828        var btn = function(id,cmd , toggle, handler, html){
25829        
25830             var  event = toggle ? 'toggle' : 'click';
25831        
25832             var a = {
25833                 size : 'sm',
25834                 xtype: 'Button',
25835                 xns: Roo.bootstrap,
25836                 //glyphicon : id,
25837                 fa: id,
25838                 cmd : id || cmd,
25839                 enableToggle:toggle !== false,
25840                 html : html || '',
25841                 pressed : toggle ? false : null,
25842                 listeners : {}
25843             };
25844             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25845                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25846             };
25847             children.push(a);
25848             return a;
25849        }
25850        
25851     //    var cb_box = function...
25852         
25853         var style = {
25854                 xtype: 'Button',
25855                 size : 'sm',
25856                 xns: Roo.bootstrap,
25857                 fa : 'font',
25858                 //html : 'submit'
25859                 menu : {
25860                     xtype: 'Menu',
25861                     xns: Roo.bootstrap,
25862                     items:  []
25863                 }
25864         };
25865         Roo.each(this.formats, function(f) {
25866             style.menu.items.push({
25867                 xtype :'MenuItem',
25868                 xns: Roo.bootstrap,
25869                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25870                 tagname : f,
25871                 listeners : {
25872                     click : function()
25873                     {
25874                         editorcore.insertTag(this.tagname);
25875                         editor.focus();
25876                     }
25877                 }
25878                 
25879             });
25880         });
25881         children.push(style);   
25882         
25883         btn('bold',false,true);
25884         btn('italic',false,true);
25885         btn('align-left', 'justifyleft',true);
25886         btn('align-center', 'justifycenter',true);
25887         btn('align-right' , 'justifyright',true);
25888         btn('link', false, false, function(btn) {
25889             //Roo.log("create link?");
25890             var url = prompt(this.createLinkText, this.defaultLinkValue);
25891             if(url && url != 'http:/'+'/'){
25892                 this.editorcore.relayCmd('createlink', url);
25893             }
25894         }),
25895         btn('list','insertunorderedlist',true);
25896         btn('pencil', false,true, function(btn){
25897                 Roo.log(this);
25898                 this.toggleSourceEdit(btn.pressed);
25899         });
25900         
25901         if (this.editor.btns.length > 0) {
25902             for (var i = 0; i<this.editor.btns.length; i++) {
25903                 children.push(this.editor.btns[i]);
25904             }
25905         }
25906         
25907         /*
25908         var cog = {
25909                 xtype: 'Button',
25910                 size : 'sm',
25911                 xns: Roo.bootstrap,
25912                 glyphicon : 'cog',
25913                 //html : 'submit'
25914                 menu : {
25915                     xtype: 'Menu',
25916                     xns: Roo.bootstrap,
25917                     items:  []
25918                 }
25919         };
25920         
25921         cog.menu.items.push({
25922             xtype :'MenuItem',
25923             xns: Roo.bootstrap,
25924             html : Clean styles,
25925             tagname : f,
25926             listeners : {
25927                 click : function()
25928                 {
25929                     editorcore.insertTag(this.tagname);
25930                     editor.focus();
25931                 }
25932             }
25933             
25934         });
25935        */
25936         
25937          
25938        this.xtype = 'NavSimplebar';
25939         
25940         for(var i=0;i< children.length;i++) {
25941             
25942             this.buttons.add(this.addxtypeChild(children[i]));
25943             
25944         }
25945         
25946         editor.on('editorevent', this.updateToolbar, this);
25947     },
25948     onBtnClick : function(id)
25949     {
25950        this.editorcore.relayCmd(id);
25951        this.editorcore.focus();
25952     },
25953     
25954     /**
25955      * Protected method that will not generally be called directly. It triggers
25956      * a toolbar update by reading the markup state of the current selection in the editor.
25957      */
25958     updateToolbar: function(){
25959
25960         if(!this.editorcore.activated){
25961             this.editor.onFirstFocus(); // is this neeed?
25962             return;
25963         }
25964
25965         var btns = this.buttons; 
25966         var doc = this.editorcore.doc;
25967         btns.get('bold').setActive(doc.queryCommandState('bold'));
25968         btns.get('italic').setActive(doc.queryCommandState('italic'));
25969         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25970         
25971         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25972         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25973         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25974         
25975         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25976         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25977          /*
25978         
25979         var ans = this.editorcore.getAllAncestors();
25980         if (this.formatCombo) {
25981             
25982             
25983             var store = this.formatCombo.store;
25984             this.formatCombo.setValue("");
25985             for (var i =0; i < ans.length;i++) {
25986                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25987                     // select it..
25988                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25989                     break;
25990                 }
25991             }
25992         }
25993         
25994         
25995         
25996         // hides menus... - so this cant be on a menu...
25997         Roo.bootstrap.MenuMgr.hideAll();
25998         */
25999         Roo.bootstrap.MenuMgr.hideAll();
26000         //this.editorsyncValue();
26001     },
26002     onFirstFocus: function() {
26003         this.buttons.each(function(item){
26004            item.enable();
26005         });
26006     },
26007     toggleSourceEdit : function(sourceEditMode){
26008         
26009           
26010         if(sourceEditMode){
26011             Roo.log("disabling buttons");
26012            this.buttons.each( function(item){
26013                 if(item.cmd != 'pencil'){
26014                     item.disable();
26015                 }
26016             });
26017           
26018         }else{
26019             Roo.log("enabling buttons");
26020             if(this.editorcore.initialized){
26021                 this.buttons.each( function(item){
26022                     item.enable();
26023                 });
26024             }
26025             
26026         }
26027         Roo.log("calling toggole on editor");
26028         // tell the editor that it's been pressed..
26029         this.editor.toggleSourceEdit(sourceEditMode);
26030        
26031     }
26032 });
26033
26034
26035
26036
26037
26038 /**
26039  * @class Roo.bootstrap.Table.AbstractSelectionModel
26040  * @extends Roo.util.Observable
26041  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26042  * implemented by descendant classes.  This class should not be directly instantiated.
26043  * @constructor
26044  */
26045 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26046     this.locked = false;
26047     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26048 };
26049
26050
26051 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26052     /** @ignore Called by the grid automatically. Do not call directly. */
26053     init : function(grid){
26054         this.grid = grid;
26055         this.initEvents();
26056     },
26057
26058     /**
26059      * Locks the selections.
26060      */
26061     lock : function(){
26062         this.locked = true;
26063     },
26064
26065     /**
26066      * Unlocks the selections.
26067      */
26068     unlock : function(){
26069         this.locked = false;
26070     },
26071
26072     /**
26073      * Returns true if the selections are locked.
26074      * @return {Boolean}
26075      */
26076     isLocked : function(){
26077         return this.locked;
26078     },
26079     
26080     
26081     initEvents : function ()
26082     {
26083         
26084     }
26085 });
26086 /**
26087  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26088  * @class Roo.bootstrap.Table.RowSelectionModel
26089  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26090  * It supports multiple selections and keyboard selection/navigation. 
26091  * @constructor
26092  * @param {Object} config
26093  */
26094
26095 Roo.bootstrap.Table.RowSelectionModel = function(config){
26096     Roo.apply(this, config);
26097     this.selections = new Roo.util.MixedCollection(false, function(o){
26098         return o.id;
26099     });
26100
26101     this.last = false;
26102     this.lastActive = false;
26103
26104     this.addEvents({
26105         /**
26106              * @event selectionchange
26107              * Fires when the selection changes
26108              * @param {SelectionModel} this
26109              */
26110             "selectionchange" : true,
26111         /**
26112              * @event afterselectionchange
26113              * Fires after the selection changes (eg. by key press or clicking)
26114              * @param {SelectionModel} this
26115              */
26116             "afterselectionchange" : true,
26117         /**
26118              * @event beforerowselect
26119              * Fires when a row is selected being selected, return false to cancel.
26120              * @param {SelectionModel} this
26121              * @param {Number} rowIndex The selected index
26122              * @param {Boolean} keepExisting False if other selections will be cleared
26123              */
26124             "beforerowselect" : true,
26125         /**
26126              * @event rowselect
26127              * Fires when a row is selected.
26128              * @param {SelectionModel} this
26129              * @param {Number} rowIndex The selected index
26130              * @param {Roo.data.Record} r The record
26131              */
26132             "rowselect" : true,
26133         /**
26134              * @event rowdeselect
26135              * Fires when a row is deselected.
26136              * @param {SelectionModel} this
26137              * @param {Number} rowIndex The selected index
26138              */
26139         "rowdeselect" : true
26140     });
26141     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26142     this.locked = false;
26143  };
26144
26145 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26146     /**
26147      * @cfg {Boolean} singleSelect
26148      * True to allow selection of only one row at a time (defaults to false)
26149      */
26150     singleSelect : false,
26151
26152     // private
26153     initEvents : function()
26154     {
26155
26156         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26157         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26158         //}else{ // allow click to work like normal
26159          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26160         //}
26161         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26162         this.grid.on("rowclick", this.handleMouseDown, this);
26163         
26164         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26165             "up" : function(e){
26166                 if(!e.shiftKey){
26167                     this.selectPrevious(e.shiftKey);
26168                 }else if(this.last !== false && this.lastActive !== false){
26169                     var last = this.last;
26170                     this.selectRange(this.last,  this.lastActive-1);
26171                     this.grid.getView().focusRow(this.lastActive);
26172                     if(last !== false){
26173                         this.last = last;
26174                     }
26175                 }else{
26176                     this.selectFirstRow();
26177                 }
26178                 this.fireEvent("afterselectionchange", this);
26179             },
26180             "down" : function(e){
26181                 if(!e.shiftKey){
26182                     this.selectNext(e.shiftKey);
26183                 }else if(this.last !== false && this.lastActive !== false){
26184                     var last = this.last;
26185                     this.selectRange(this.last,  this.lastActive+1);
26186                     this.grid.getView().focusRow(this.lastActive);
26187                     if(last !== false){
26188                         this.last = last;
26189                     }
26190                 }else{
26191                     this.selectFirstRow();
26192                 }
26193                 this.fireEvent("afterselectionchange", this);
26194             },
26195             scope: this
26196         });
26197         this.grid.store.on('load', function(){
26198             this.selections.clear();
26199         },this);
26200         /*
26201         var view = this.grid.view;
26202         view.on("refresh", this.onRefresh, this);
26203         view.on("rowupdated", this.onRowUpdated, this);
26204         view.on("rowremoved", this.onRemove, this);
26205         */
26206     },
26207
26208     // private
26209     onRefresh : function()
26210     {
26211         var ds = this.grid.store, i, v = this.grid.view;
26212         var s = this.selections;
26213         s.each(function(r){
26214             if((i = ds.indexOfId(r.id)) != -1){
26215                 v.onRowSelect(i);
26216             }else{
26217                 s.remove(r);
26218             }
26219         });
26220     },
26221
26222     // private
26223     onRemove : function(v, index, r){
26224         this.selections.remove(r);
26225     },
26226
26227     // private
26228     onRowUpdated : function(v, index, r){
26229         if(this.isSelected(r)){
26230             v.onRowSelect(index);
26231         }
26232     },
26233
26234     /**
26235      * Select records.
26236      * @param {Array} records The records to select
26237      * @param {Boolean} keepExisting (optional) True to keep existing selections
26238      */
26239     selectRecords : function(records, keepExisting)
26240     {
26241         if(!keepExisting){
26242             this.clearSelections();
26243         }
26244             var ds = this.grid.store;
26245         for(var i = 0, len = records.length; i < len; i++){
26246             this.selectRow(ds.indexOf(records[i]), true);
26247         }
26248     },
26249
26250     /**
26251      * Gets the number of selected rows.
26252      * @return {Number}
26253      */
26254     getCount : function(){
26255         return this.selections.length;
26256     },
26257
26258     /**
26259      * Selects the first row in the grid.
26260      */
26261     selectFirstRow : function(){
26262         this.selectRow(0);
26263     },
26264
26265     /**
26266      * Select the last row.
26267      * @param {Boolean} keepExisting (optional) True to keep existing selections
26268      */
26269     selectLastRow : function(keepExisting){
26270         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26271         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26272     },
26273
26274     /**
26275      * Selects the row immediately following the last selected row.
26276      * @param {Boolean} keepExisting (optional) True to keep existing selections
26277      */
26278     selectNext : function(keepExisting)
26279     {
26280             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26281             this.selectRow(this.last+1, keepExisting);
26282             this.grid.getView().focusRow(this.last);
26283         }
26284     },
26285
26286     /**
26287      * Selects the row that precedes the last selected row.
26288      * @param {Boolean} keepExisting (optional) True to keep existing selections
26289      */
26290     selectPrevious : function(keepExisting){
26291         if(this.last){
26292             this.selectRow(this.last-1, keepExisting);
26293             this.grid.getView().focusRow(this.last);
26294         }
26295     },
26296
26297     /**
26298      * Returns the selected records
26299      * @return {Array} Array of selected records
26300      */
26301     getSelections : function(){
26302         return [].concat(this.selections.items);
26303     },
26304
26305     /**
26306      * Returns the first selected record.
26307      * @return {Record}
26308      */
26309     getSelected : function(){
26310         return this.selections.itemAt(0);
26311     },
26312
26313
26314     /**
26315      * Clears all selections.
26316      */
26317     clearSelections : function(fast)
26318     {
26319         if(this.locked) {
26320             return;
26321         }
26322         if(fast !== true){
26323                 var ds = this.grid.store;
26324             var s = this.selections;
26325             s.each(function(r){
26326                 this.deselectRow(ds.indexOfId(r.id));
26327             }, this);
26328             s.clear();
26329         }else{
26330             this.selections.clear();
26331         }
26332         this.last = false;
26333     },
26334
26335
26336     /**
26337      * Selects all rows.
26338      */
26339     selectAll : function(){
26340         if(this.locked) {
26341             return;
26342         }
26343         this.selections.clear();
26344         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26345             this.selectRow(i, true);
26346         }
26347     },
26348
26349     /**
26350      * Returns True if there is a selection.
26351      * @return {Boolean}
26352      */
26353     hasSelection : function(){
26354         return this.selections.length > 0;
26355     },
26356
26357     /**
26358      * Returns True if the specified row is selected.
26359      * @param {Number/Record} record The record or index of the record to check
26360      * @return {Boolean}
26361      */
26362     isSelected : function(index){
26363             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26364         return (r && this.selections.key(r.id) ? true : false);
26365     },
26366
26367     /**
26368      * Returns True if the specified record id is selected.
26369      * @param {String} id The id of record to check
26370      * @return {Boolean}
26371      */
26372     isIdSelected : function(id){
26373         return (this.selections.key(id) ? true : false);
26374     },
26375
26376
26377     // private
26378     handleMouseDBClick : function(e, t){
26379         
26380     },
26381     // private
26382     handleMouseDown : function(e, t)
26383     {
26384             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26385         if(this.isLocked() || rowIndex < 0 ){
26386             return;
26387         };
26388         if(e.shiftKey && this.last !== false){
26389             var last = this.last;
26390             this.selectRange(last, rowIndex, e.ctrlKey);
26391             this.last = last; // reset the last
26392             t.focus();
26393     
26394         }else{
26395             var isSelected = this.isSelected(rowIndex);
26396             //Roo.log("select row:" + rowIndex);
26397             if(isSelected){
26398                 this.deselectRow(rowIndex);
26399             } else {
26400                         this.selectRow(rowIndex, true);
26401             }
26402     
26403             /*
26404                 if(e.button !== 0 && isSelected){
26405                 alert('rowIndex 2: ' + rowIndex);
26406                     view.focusRow(rowIndex);
26407                 }else if(e.ctrlKey && isSelected){
26408                     this.deselectRow(rowIndex);
26409                 }else if(!isSelected){
26410                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26411                     view.focusRow(rowIndex);
26412                 }
26413             */
26414         }
26415         this.fireEvent("afterselectionchange", this);
26416     },
26417     // private
26418     handleDragableRowClick :  function(grid, rowIndex, e) 
26419     {
26420         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26421             this.selectRow(rowIndex, false);
26422             grid.view.focusRow(rowIndex);
26423              this.fireEvent("afterselectionchange", this);
26424         }
26425     },
26426     
26427     /**
26428      * Selects multiple rows.
26429      * @param {Array} rows Array of the indexes of the row to select
26430      * @param {Boolean} keepExisting (optional) True to keep existing selections
26431      */
26432     selectRows : function(rows, keepExisting){
26433         if(!keepExisting){
26434             this.clearSelections();
26435         }
26436         for(var i = 0, len = rows.length; i < len; i++){
26437             this.selectRow(rows[i], true);
26438         }
26439     },
26440
26441     /**
26442      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26443      * @param {Number} startRow The index of the first row in the range
26444      * @param {Number} endRow The index of the last row in the range
26445      * @param {Boolean} keepExisting (optional) True to retain existing selections
26446      */
26447     selectRange : function(startRow, endRow, keepExisting){
26448         if(this.locked) {
26449             return;
26450         }
26451         if(!keepExisting){
26452             this.clearSelections();
26453         }
26454         if(startRow <= endRow){
26455             for(var i = startRow; i <= endRow; i++){
26456                 this.selectRow(i, true);
26457             }
26458         }else{
26459             for(var i = startRow; i >= endRow; i--){
26460                 this.selectRow(i, true);
26461             }
26462         }
26463     },
26464
26465     /**
26466      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26467      * @param {Number} startRow The index of the first row in the range
26468      * @param {Number} endRow The index of the last row in the range
26469      */
26470     deselectRange : function(startRow, endRow, preventViewNotify){
26471         if(this.locked) {
26472             return;
26473         }
26474         for(var i = startRow; i <= endRow; i++){
26475             this.deselectRow(i, preventViewNotify);
26476         }
26477     },
26478
26479     /**
26480      * Selects a row.
26481      * @param {Number} row The index of the row to select
26482      * @param {Boolean} keepExisting (optional) True to keep existing selections
26483      */
26484     selectRow : function(index, keepExisting, preventViewNotify)
26485     {
26486             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26487             return;
26488         }
26489         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26490             if(!keepExisting || this.singleSelect){
26491                 this.clearSelections();
26492             }
26493             
26494             var r = this.grid.store.getAt(index);
26495             //console.log('selectRow - record id :' + r.id);
26496             
26497             this.selections.add(r);
26498             this.last = this.lastActive = index;
26499             if(!preventViewNotify){
26500                 var proxy = new Roo.Element(
26501                                 this.grid.getRowDom(index)
26502                 );
26503                 proxy.addClass('bg-info info');
26504             }
26505             this.fireEvent("rowselect", this, index, r);
26506             this.fireEvent("selectionchange", this);
26507         }
26508     },
26509
26510     /**
26511      * Deselects a row.
26512      * @param {Number} row The index of the row to deselect
26513      */
26514     deselectRow : function(index, preventViewNotify)
26515     {
26516         if(this.locked) {
26517             return;
26518         }
26519         if(this.last == index){
26520             this.last = false;
26521         }
26522         if(this.lastActive == index){
26523             this.lastActive = false;
26524         }
26525         
26526         var r = this.grid.store.getAt(index);
26527         if (!r) {
26528             return;
26529         }
26530         
26531         this.selections.remove(r);
26532         //.console.log('deselectRow - record id :' + r.id);
26533         if(!preventViewNotify){
26534         
26535             var proxy = new Roo.Element(
26536                 this.grid.getRowDom(index)
26537             );
26538             proxy.removeClass('bg-info info');
26539         }
26540         this.fireEvent("rowdeselect", this, index);
26541         this.fireEvent("selectionchange", this);
26542     },
26543
26544     // private
26545     restoreLast : function(){
26546         if(this._last){
26547             this.last = this._last;
26548         }
26549     },
26550
26551     // private
26552     acceptsNav : function(row, col, cm){
26553         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26554     },
26555
26556     // private
26557     onEditorKey : function(field, e){
26558         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26559         if(k == e.TAB){
26560             e.stopEvent();
26561             ed.completeEdit();
26562             if(e.shiftKey){
26563                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26564             }else{
26565                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26566             }
26567         }else if(k == e.ENTER && !e.ctrlKey){
26568             e.stopEvent();
26569             ed.completeEdit();
26570             if(e.shiftKey){
26571                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26572             }else{
26573                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26574             }
26575         }else if(k == e.ESC){
26576             ed.cancelEdit();
26577         }
26578         if(newCell){
26579             g.startEditing(newCell[0], newCell[1]);
26580         }
26581     }
26582 });
26583 /*
26584  * Based on:
26585  * Ext JS Library 1.1.1
26586  * Copyright(c) 2006-2007, Ext JS, LLC.
26587  *
26588  * Originally Released Under LGPL - original licence link has changed is not relivant.
26589  *
26590  * Fork - LGPL
26591  * <script type="text/javascript">
26592  */
26593  
26594 /**
26595  * @class Roo.bootstrap.PagingToolbar
26596  * @extends Roo.bootstrap.NavSimplebar
26597  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26598  * @constructor
26599  * Create a new PagingToolbar
26600  * @param {Object} config The config object
26601  * @param {Roo.data.Store} store
26602  */
26603 Roo.bootstrap.PagingToolbar = function(config)
26604 {
26605     // old args format still supported... - xtype is prefered..
26606         // created from xtype...
26607     
26608     this.ds = config.dataSource;
26609     
26610     if (config.store && !this.ds) {
26611         this.store= Roo.factory(config.store, Roo.data);
26612         this.ds = this.store;
26613         this.ds.xmodule = this.xmodule || false;
26614     }
26615     
26616     this.toolbarItems = [];
26617     if (config.items) {
26618         this.toolbarItems = config.items;
26619     }
26620     
26621     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26622     
26623     this.cursor = 0;
26624     
26625     if (this.ds) { 
26626         this.bind(this.ds);
26627     }
26628     
26629     if (Roo.bootstrap.version == 4) {
26630         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26631     } else {
26632         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26633     }
26634     
26635 };
26636
26637 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26638     /**
26639      * @cfg {Roo.data.Store} dataSource
26640      * The underlying data store providing the paged data
26641      */
26642     /**
26643      * @cfg {String/HTMLElement/Element} container
26644      * container The id or element that will contain the toolbar
26645      */
26646     /**
26647      * @cfg {Boolean} displayInfo
26648      * True to display the displayMsg (defaults to false)
26649      */
26650     /**
26651      * @cfg {Number} pageSize
26652      * The number of records to display per page (defaults to 20)
26653      */
26654     pageSize: 20,
26655     /**
26656      * @cfg {String} displayMsg
26657      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26658      */
26659     displayMsg : 'Displaying {0} - {1} of {2}',
26660     /**
26661      * @cfg {String} emptyMsg
26662      * The message to display when no records are found (defaults to "No data to display")
26663      */
26664     emptyMsg : 'No data to display',
26665     /**
26666      * Customizable piece of the default paging text (defaults to "Page")
26667      * @type String
26668      */
26669     beforePageText : "Page",
26670     /**
26671      * Customizable piece of the default paging text (defaults to "of %0")
26672      * @type String
26673      */
26674     afterPageText : "of {0}",
26675     /**
26676      * Customizable piece of the default paging text (defaults to "First Page")
26677      * @type String
26678      */
26679     firstText : "First Page",
26680     /**
26681      * Customizable piece of the default paging text (defaults to "Previous Page")
26682      * @type String
26683      */
26684     prevText : "Previous Page",
26685     /**
26686      * Customizable piece of the default paging text (defaults to "Next Page")
26687      * @type String
26688      */
26689     nextText : "Next Page",
26690     /**
26691      * Customizable piece of the default paging text (defaults to "Last Page")
26692      * @type String
26693      */
26694     lastText : "Last Page",
26695     /**
26696      * Customizable piece of the default paging text (defaults to "Refresh")
26697      * @type String
26698      */
26699     refreshText : "Refresh",
26700
26701     buttons : false,
26702     // private
26703     onRender : function(ct, position) 
26704     {
26705         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26706         this.navgroup.parentId = this.id;
26707         this.navgroup.onRender(this.el, null);
26708         // add the buttons to the navgroup
26709         
26710         if(this.displayInfo){
26711             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26712             this.displayEl = this.el.select('.x-paging-info', true).first();
26713 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26714 //            this.displayEl = navel.el.select('span',true).first();
26715         }
26716         
26717         var _this = this;
26718         
26719         if(this.buttons){
26720             Roo.each(_this.buttons, function(e){ // this might need to use render????
26721                Roo.factory(e).render(_this.el);
26722             });
26723         }
26724             
26725         Roo.each(_this.toolbarItems, function(e) {
26726             _this.navgroup.addItem(e);
26727         });
26728         
26729         
26730         this.first = this.navgroup.addItem({
26731             tooltip: this.firstText,
26732             cls: "prev btn-outline-secondary",
26733             html : ' <i class="fa fa-step-backward"></i>',
26734             disabled: true,
26735             preventDefault: true,
26736             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26737         });
26738         
26739         this.prev =  this.navgroup.addItem({
26740             tooltip: this.prevText,
26741             cls: "prev btn-outline-secondary",
26742             html : ' <i class="fa fa-backward"></i>',
26743             disabled: true,
26744             preventDefault: true,
26745             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26746         });
26747     //this.addSeparator();
26748         
26749         
26750         var field = this.navgroup.addItem( {
26751             tagtype : 'span',
26752             cls : 'x-paging-position  btn-outline-secondary',
26753              disabled: true,
26754             html : this.beforePageText  +
26755                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26756                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26757          } ); //?? escaped?
26758         
26759         this.field = field.el.select('input', true).first();
26760         this.field.on("keydown", this.onPagingKeydown, this);
26761         this.field.on("focus", function(){this.dom.select();});
26762     
26763     
26764         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26765         //this.field.setHeight(18);
26766         //this.addSeparator();
26767         this.next = this.navgroup.addItem({
26768             tooltip: this.nextText,
26769             cls: "next btn-outline-secondary",
26770             html : ' <i class="fa fa-forward"></i>',
26771             disabled: true,
26772             preventDefault: true,
26773             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26774         });
26775         this.last = this.navgroup.addItem({
26776             tooltip: this.lastText,
26777             html : ' <i class="fa fa-step-forward"></i>',
26778             cls: "next btn-outline-secondary",
26779             disabled: true,
26780             preventDefault: true,
26781             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26782         });
26783     //this.addSeparator();
26784         this.loading = this.navgroup.addItem({
26785             tooltip: this.refreshText,
26786             cls: "btn-outline-secondary",
26787             html : ' <i class="fa fa-refresh"></i>',
26788             preventDefault: true,
26789             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26790         });
26791         
26792     },
26793
26794     // private
26795     updateInfo : function(){
26796         if(this.displayEl){
26797             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26798             var msg = count == 0 ?
26799                 this.emptyMsg :
26800                 String.format(
26801                     this.displayMsg,
26802                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26803                 );
26804             this.displayEl.update(msg);
26805         }
26806     },
26807
26808     // private
26809     onLoad : function(ds, r, o)
26810     {
26811         this.cursor = o.params.start ? o.params.start : 0;
26812         
26813         var d = this.getPageData(),
26814             ap = d.activePage,
26815             ps = d.pages;
26816         
26817         
26818         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26819         this.field.dom.value = ap;
26820         this.first.setDisabled(ap == 1);
26821         this.prev.setDisabled(ap == 1);
26822         this.next.setDisabled(ap == ps);
26823         this.last.setDisabled(ap == ps);
26824         this.loading.enable();
26825         this.updateInfo();
26826     },
26827
26828     // private
26829     getPageData : function(){
26830         var total = this.ds.getTotalCount();
26831         return {
26832             total : total,
26833             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26834             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26835         };
26836     },
26837
26838     // private
26839     onLoadError : function(){
26840         this.loading.enable();
26841     },
26842
26843     // private
26844     onPagingKeydown : function(e){
26845         var k = e.getKey();
26846         var d = this.getPageData();
26847         if(k == e.RETURN){
26848             var v = this.field.dom.value, pageNum;
26849             if(!v || isNaN(pageNum = parseInt(v, 10))){
26850                 this.field.dom.value = d.activePage;
26851                 return;
26852             }
26853             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26854             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26855             e.stopEvent();
26856         }
26857         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))
26858         {
26859           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26860           this.field.dom.value = pageNum;
26861           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26862           e.stopEvent();
26863         }
26864         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26865         {
26866           var v = this.field.dom.value, pageNum; 
26867           var increment = (e.shiftKey) ? 10 : 1;
26868           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26869                 increment *= -1;
26870           }
26871           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26872             this.field.dom.value = d.activePage;
26873             return;
26874           }
26875           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26876           {
26877             this.field.dom.value = parseInt(v, 10) + increment;
26878             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26879             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26880           }
26881           e.stopEvent();
26882         }
26883     },
26884
26885     // private
26886     beforeLoad : function(){
26887         if(this.loading){
26888             this.loading.disable();
26889         }
26890     },
26891
26892     // private
26893     onClick : function(which){
26894         
26895         var ds = this.ds;
26896         if (!ds) {
26897             return;
26898         }
26899         
26900         switch(which){
26901             case "first":
26902                 ds.load({params:{start: 0, limit: this.pageSize}});
26903             break;
26904             case "prev":
26905                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26906             break;
26907             case "next":
26908                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26909             break;
26910             case "last":
26911                 var total = ds.getTotalCount();
26912                 var extra = total % this.pageSize;
26913                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26914                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26915             break;
26916             case "refresh":
26917                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26918             break;
26919         }
26920     },
26921
26922     /**
26923      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26924      * @param {Roo.data.Store} store The data store to unbind
26925      */
26926     unbind : function(ds){
26927         ds.un("beforeload", this.beforeLoad, this);
26928         ds.un("load", this.onLoad, this);
26929         ds.un("loadexception", this.onLoadError, this);
26930         ds.un("remove", this.updateInfo, this);
26931         ds.un("add", this.updateInfo, this);
26932         this.ds = undefined;
26933     },
26934
26935     /**
26936      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26937      * @param {Roo.data.Store} store The data store to bind
26938      */
26939     bind : function(ds){
26940         ds.on("beforeload", this.beforeLoad, this);
26941         ds.on("load", this.onLoad, this);
26942         ds.on("loadexception", this.onLoadError, this);
26943         ds.on("remove", this.updateInfo, this);
26944         ds.on("add", this.updateInfo, this);
26945         this.ds = ds;
26946     }
26947 });/*
26948  * - LGPL
26949  *
26950  * element
26951  * 
26952  */
26953
26954 /**
26955  * @class Roo.bootstrap.MessageBar
26956  * @extends Roo.bootstrap.Component
26957  * Bootstrap MessageBar class
26958  * @cfg {String} html contents of the MessageBar
26959  * @cfg {String} weight (info | success | warning | danger) default info
26960  * @cfg {String} beforeClass insert the bar before the given class
26961  * @cfg {Boolean} closable (true | false) default false
26962  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26963  * 
26964  * @constructor
26965  * Create a new Element
26966  * @param {Object} config The config object
26967  */
26968
26969 Roo.bootstrap.MessageBar = function(config){
26970     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26971 };
26972
26973 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26974     
26975     html: '',
26976     weight: 'info',
26977     closable: false,
26978     fixed: false,
26979     beforeClass: 'bootstrap-sticky-wrap',
26980     
26981     getAutoCreate : function(){
26982         
26983         var cfg = {
26984             tag: 'div',
26985             cls: 'alert alert-dismissable alert-' + this.weight,
26986             cn: [
26987                 {
26988                     tag: 'span',
26989                     cls: 'message',
26990                     html: this.html || ''
26991                 }
26992             ]
26993         };
26994         
26995         if(this.fixed){
26996             cfg.cls += ' alert-messages-fixed';
26997         }
26998         
26999         if(this.closable){
27000             cfg.cn.push({
27001                 tag: 'button',
27002                 cls: 'close',
27003                 html: 'x'
27004             });
27005         }
27006         
27007         return cfg;
27008     },
27009     
27010     onRender : function(ct, position)
27011     {
27012         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27013         
27014         if(!this.el){
27015             var cfg = Roo.apply({},  this.getAutoCreate());
27016             cfg.id = Roo.id();
27017             
27018             if (this.cls) {
27019                 cfg.cls += ' ' + this.cls;
27020             }
27021             if (this.style) {
27022                 cfg.style = this.style;
27023             }
27024             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27025             
27026             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27027         }
27028         
27029         this.el.select('>button.close').on('click', this.hide, this);
27030         
27031     },
27032     
27033     show : function()
27034     {
27035         if (!this.rendered) {
27036             this.render();
27037         }
27038         
27039         this.el.show();
27040         
27041         this.fireEvent('show', this);
27042         
27043     },
27044     
27045     hide : function()
27046     {
27047         if (!this.rendered) {
27048             this.render();
27049         }
27050         
27051         this.el.hide();
27052         
27053         this.fireEvent('hide', this);
27054     },
27055     
27056     update : function()
27057     {
27058 //        var e = this.el.dom.firstChild;
27059 //        
27060 //        if(this.closable){
27061 //            e = e.nextSibling;
27062 //        }
27063 //        
27064 //        e.data = this.html || '';
27065
27066         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27067     }
27068    
27069 });
27070
27071  
27072
27073      /*
27074  * - LGPL
27075  *
27076  * Graph
27077  * 
27078  */
27079
27080
27081 /**
27082  * @class Roo.bootstrap.Graph
27083  * @extends Roo.bootstrap.Component
27084  * Bootstrap Graph class
27085 > Prameters
27086  -sm {number} sm 4
27087  -md {number} md 5
27088  @cfg {String} graphtype  bar | vbar | pie
27089  @cfg {number} g_x coodinator | centre x (pie)
27090  @cfg {number} g_y coodinator | centre y (pie)
27091  @cfg {number} g_r radius (pie)
27092  @cfg {number} g_height height of the chart (respected by all elements in the set)
27093  @cfg {number} g_width width of the chart (respected by all elements in the set)
27094  @cfg {Object} title The title of the chart
27095     
27096  -{Array}  values
27097  -opts (object) options for the chart 
27098      o {
27099      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27100      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27101      o vgutter (number)
27102      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.
27103      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27104      o to
27105      o stretch (boolean)
27106      o }
27107  -opts (object) options for the pie
27108      o{
27109      o cut
27110      o startAngle (number)
27111      o endAngle (number)
27112      } 
27113  *
27114  * @constructor
27115  * Create a new Input
27116  * @param {Object} config The config object
27117  */
27118
27119 Roo.bootstrap.Graph = function(config){
27120     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27121     
27122     this.addEvents({
27123         // img events
27124         /**
27125          * @event click
27126          * The img click event for the img.
27127          * @param {Roo.EventObject} e
27128          */
27129         "click" : true
27130     });
27131 };
27132
27133 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27134     
27135     sm: 4,
27136     md: 5,
27137     graphtype: 'bar',
27138     g_height: 250,
27139     g_width: 400,
27140     g_x: 50,
27141     g_y: 50,
27142     g_r: 30,
27143     opts:{
27144         //g_colors: this.colors,
27145         g_type: 'soft',
27146         g_gutter: '20%'
27147
27148     },
27149     title : false,
27150
27151     getAutoCreate : function(){
27152         
27153         var cfg = {
27154             tag: 'div',
27155             html : null
27156         };
27157         
27158         
27159         return  cfg;
27160     },
27161
27162     onRender : function(ct,position){
27163         
27164         
27165         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27166         
27167         if (typeof(Raphael) == 'undefined') {
27168             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27169             return;
27170         }
27171         
27172         this.raphael = Raphael(this.el.dom);
27173         
27174                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27175                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27176                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27177                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27178                 /*
27179                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27180                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27181                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27182                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27183                 
27184                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27185                 r.barchart(330, 10, 300, 220, data1);
27186                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27187                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27188                 */
27189                 
27190                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27191                 // r.barchart(30, 30, 560, 250,  xdata, {
27192                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27193                 //     axis : "0 0 1 1",
27194                 //     axisxlabels :  xdata
27195                 //     //yvalues : cols,
27196                    
27197                 // });
27198 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27199 //        
27200 //        this.load(null,xdata,{
27201 //                axis : "0 0 1 1",
27202 //                axisxlabels :  xdata
27203 //                });
27204
27205     },
27206
27207     load : function(graphtype,xdata,opts)
27208     {
27209         this.raphael.clear();
27210         if(!graphtype) {
27211             graphtype = this.graphtype;
27212         }
27213         if(!opts){
27214             opts = this.opts;
27215         }
27216         var r = this.raphael,
27217             fin = function () {
27218                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27219             },
27220             fout = function () {
27221                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27222             },
27223             pfin = function() {
27224                 this.sector.stop();
27225                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27226
27227                 if (this.label) {
27228                     this.label[0].stop();
27229                     this.label[0].attr({ r: 7.5 });
27230                     this.label[1].attr({ "font-weight": 800 });
27231                 }
27232             },
27233             pfout = function() {
27234                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27235
27236                 if (this.label) {
27237                     this.label[0].animate({ r: 5 }, 500, "bounce");
27238                     this.label[1].attr({ "font-weight": 400 });
27239                 }
27240             };
27241
27242         switch(graphtype){
27243             case 'bar':
27244                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27245                 break;
27246             case 'hbar':
27247                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27248                 break;
27249             case 'pie':
27250 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27251 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27252 //            
27253                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27254                 
27255                 break;
27256
27257         }
27258         
27259         if(this.title){
27260             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27261         }
27262         
27263     },
27264     
27265     setTitle: function(o)
27266     {
27267         this.title = o;
27268     },
27269     
27270     initEvents: function() {
27271         
27272         if(!this.href){
27273             this.el.on('click', this.onClick, this);
27274         }
27275     },
27276     
27277     onClick : function(e)
27278     {
27279         Roo.log('img onclick');
27280         this.fireEvent('click', this, e);
27281     }
27282    
27283 });
27284
27285  
27286 /*
27287  * - LGPL
27288  *
27289  * numberBox
27290  * 
27291  */
27292 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27293
27294 /**
27295  * @class Roo.bootstrap.dash.NumberBox
27296  * @extends Roo.bootstrap.Component
27297  * Bootstrap NumberBox class
27298  * @cfg {String} headline Box headline
27299  * @cfg {String} content Box content
27300  * @cfg {String} icon Box icon
27301  * @cfg {String} footer Footer text
27302  * @cfg {String} fhref Footer href
27303  * 
27304  * @constructor
27305  * Create a new NumberBox
27306  * @param {Object} config The config object
27307  */
27308
27309
27310 Roo.bootstrap.dash.NumberBox = function(config){
27311     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27312     
27313 };
27314
27315 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27316     
27317     headline : '',
27318     content : '',
27319     icon : '',
27320     footer : '',
27321     fhref : '',
27322     ficon : '',
27323     
27324     getAutoCreate : function(){
27325         
27326         var cfg = {
27327             tag : 'div',
27328             cls : 'small-box ',
27329             cn : [
27330                 {
27331                     tag : 'div',
27332                     cls : 'inner',
27333                     cn :[
27334                         {
27335                             tag : 'h3',
27336                             cls : 'roo-headline',
27337                             html : this.headline
27338                         },
27339                         {
27340                             tag : 'p',
27341                             cls : 'roo-content',
27342                             html : this.content
27343                         }
27344                     ]
27345                 }
27346             ]
27347         };
27348         
27349         if(this.icon){
27350             cfg.cn.push({
27351                 tag : 'div',
27352                 cls : 'icon',
27353                 cn :[
27354                     {
27355                         tag : 'i',
27356                         cls : 'ion ' + this.icon
27357                     }
27358                 ]
27359             });
27360         }
27361         
27362         if(this.footer){
27363             var footer = {
27364                 tag : 'a',
27365                 cls : 'small-box-footer',
27366                 href : this.fhref || '#',
27367                 html : this.footer
27368             };
27369             
27370             cfg.cn.push(footer);
27371             
27372         }
27373         
27374         return  cfg;
27375     },
27376
27377     onRender : function(ct,position){
27378         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27379
27380
27381        
27382                 
27383     },
27384
27385     setHeadline: function (value)
27386     {
27387         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27388     },
27389     
27390     setFooter: function (value, href)
27391     {
27392         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27393         
27394         if(href){
27395             this.el.select('a.small-box-footer',true).first().attr('href', href);
27396         }
27397         
27398     },
27399
27400     setContent: function (value)
27401     {
27402         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27403     },
27404
27405     initEvents: function() 
27406     {   
27407         
27408     }
27409     
27410 });
27411
27412  
27413 /*
27414  * - LGPL
27415  *
27416  * TabBox
27417  * 
27418  */
27419 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27420
27421 /**
27422  * @class Roo.bootstrap.dash.TabBox
27423  * @extends Roo.bootstrap.Component
27424  * Bootstrap TabBox class
27425  * @cfg {String} title Title of the TabBox
27426  * @cfg {String} icon Icon of the TabBox
27427  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27428  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27429  * 
27430  * @constructor
27431  * Create a new TabBox
27432  * @param {Object} config The config object
27433  */
27434
27435
27436 Roo.bootstrap.dash.TabBox = function(config){
27437     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27438     this.addEvents({
27439         // raw events
27440         /**
27441          * @event addpane
27442          * When a pane is added
27443          * @param {Roo.bootstrap.dash.TabPane} pane
27444          */
27445         "addpane" : true,
27446         /**
27447          * @event activatepane
27448          * When a pane is activated
27449          * @param {Roo.bootstrap.dash.TabPane} pane
27450          */
27451         "activatepane" : true
27452         
27453          
27454     });
27455     
27456     this.panes = [];
27457 };
27458
27459 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27460
27461     title : '',
27462     icon : false,
27463     showtabs : true,
27464     tabScrollable : false,
27465     
27466     getChildContainer : function()
27467     {
27468         return this.el.select('.tab-content', true).first();
27469     },
27470     
27471     getAutoCreate : function(){
27472         
27473         var header = {
27474             tag: 'li',
27475             cls: 'pull-left header',
27476             html: this.title,
27477             cn : []
27478         };
27479         
27480         if(this.icon){
27481             header.cn.push({
27482                 tag: 'i',
27483                 cls: 'fa ' + this.icon
27484             });
27485         }
27486         
27487         var h = {
27488             tag: 'ul',
27489             cls: 'nav nav-tabs pull-right',
27490             cn: [
27491                 header
27492             ]
27493         };
27494         
27495         if(this.tabScrollable){
27496             h = {
27497                 tag: 'div',
27498                 cls: 'tab-header',
27499                 cn: [
27500                     {
27501                         tag: 'ul',
27502                         cls: 'nav nav-tabs pull-right',
27503                         cn: [
27504                             header
27505                         ]
27506                     }
27507                 ]
27508             };
27509         }
27510         
27511         var cfg = {
27512             tag: 'div',
27513             cls: 'nav-tabs-custom',
27514             cn: [
27515                 h,
27516                 {
27517                     tag: 'div',
27518                     cls: 'tab-content no-padding',
27519                     cn: []
27520                 }
27521             ]
27522         };
27523
27524         return  cfg;
27525     },
27526     initEvents : function()
27527     {
27528         //Roo.log('add add pane handler');
27529         this.on('addpane', this.onAddPane, this);
27530     },
27531      /**
27532      * Updates the box title
27533      * @param {String} html to set the title to.
27534      */
27535     setTitle : function(value)
27536     {
27537         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27538     },
27539     onAddPane : function(pane)
27540     {
27541         this.panes.push(pane);
27542         //Roo.log('addpane');
27543         //Roo.log(pane);
27544         // tabs are rendere left to right..
27545         if(!this.showtabs){
27546             return;
27547         }
27548         
27549         var ctr = this.el.select('.nav-tabs', true).first();
27550          
27551          
27552         var existing = ctr.select('.nav-tab',true);
27553         var qty = existing.getCount();;
27554         
27555         
27556         var tab = ctr.createChild({
27557             tag : 'li',
27558             cls : 'nav-tab' + (qty ? '' : ' active'),
27559             cn : [
27560                 {
27561                     tag : 'a',
27562                     href:'#',
27563                     html : pane.title
27564                 }
27565             ]
27566         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27567         pane.tab = tab;
27568         
27569         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27570         if (!qty) {
27571             pane.el.addClass('active');
27572         }
27573         
27574                 
27575     },
27576     onTabClick : function(ev,un,ob,pane)
27577     {
27578         //Roo.log('tab - prev default');
27579         ev.preventDefault();
27580         
27581         
27582         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27583         pane.tab.addClass('active');
27584         //Roo.log(pane.title);
27585         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27586         // technically we should have a deactivate event.. but maybe add later.
27587         // and it should not de-activate the selected tab...
27588         this.fireEvent('activatepane', pane);
27589         pane.el.addClass('active');
27590         pane.fireEvent('activate');
27591         
27592         
27593     },
27594     
27595     getActivePane : function()
27596     {
27597         var r = false;
27598         Roo.each(this.panes, function(p) {
27599             if(p.el.hasClass('active')){
27600                 r = p;
27601                 return false;
27602             }
27603             
27604             return;
27605         });
27606         
27607         return r;
27608     }
27609     
27610     
27611 });
27612
27613  
27614 /*
27615  * - LGPL
27616  *
27617  * Tab pane
27618  * 
27619  */
27620 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27621 /**
27622  * @class Roo.bootstrap.TabPane
27623  * @extends Roo.bootstrap.Component
27624  * Bootstrap TabPane class
27625  * @cfg {Boolean} active (false | true) Default false
27626  * @cfg {String} title title of panel
27627
27628  * 
27629  * @constructor
27630  * Create a new TabPane
27631  * @param {Object} config The config object
27632  */
27633
27634 Roo.bootstrap.dash.TabPane = function(config){
27635     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27636     
27637     this.addEvents({
27638         // raw events
27639         /**
27640          * @event activate
27641          * When a pane is activated
27642          * @param {Roo.bootstrap.dash.TabPane} pane
27643          */
27644         "activate" : true
27645          
27646     });
27647 };
27648
27649 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27650     
27651     active : false,
27652     title : '',
27653     
27654     // the tabBox that this is attached to.
27655     tab : false,
27656      
27657     getAutoCreate : function() 
27658     {
27659         var cfg = {
27660             tag: 'div',
27661             cls: 'tab-pane'
27662         };
27663         
27664         if(this.active){
27665             cfg.cls += ' active';
27666         }
27667         
27668         return cfg;
27669     },
27670     initEvents  : function()
27671     {
27672         //Roo.log('trigger add pane handler');
27673         this.parent().fireEvent('addpane', this)
27674     },
27675     
27676      /**
27677      * Updates the tab title 
27678      * @param {String} html to set the title to.
27679      */
27680     setTitle: function(str)
27681     {
27682         if (!this.tab) {
27683             return;
27684         }
27685         this.title = str;
27686         this.tab.select('a', true).first().dom.innerHTML = str;
27687         
27688     }
27689     
27690     
27691     
27692 });
27693
27694  
27695
27696
27697  /*
27698  * - LGPL
27699  *
27700  * menu
27701  * 
27702  */
27703 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27704
27705 /**
27706  * @class Roo.bootstrap.menu.Menu
27707  * @extends Roo.bootstrap.Component
27708  * Bootstrap Menu class - container for Menu
27709  * @cfg {String} html Text of the menu
27710  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27711  * @cfg {String} icon Font awesome icon
27712  * @cfg {String} pos Menu align to (top | bottom) default bottom
27713  * 
27714  * 
27715  * @constructor
27716  * Create a new Menu
27717  * @param {Object} config The config object
27718  */
27719
27720
27721 Roo.bootstrap.menu.Menu = function(config){
27722     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27723     
27724     this.addEvents({
27725         /**
27726          * @event beforeshow
27727          * Fires before this menu is displayed
27728          * @param {Roo.bootstrap.menu.Menu} this
27729          */
27730         beforeshow : true,
27731         /**
27732          * @event beforehide
27733          * Fires before this menu is hidden
27734          * @param {Roo.bootstrap.menu.Menu} this
27735          */
27736         beforehide : true,
27737         /**
27738          * @event show
27739          * Fires after this menu is displayed
27740          * @param {Roo.bootstrap.menu.Menu} this
27741          */
27742         show : true,
27743         /**
27744          * @event hide
27745          * Fires after this menu is hidden
27746          * @param {Roo.bootstrap.menu.Menu} this
27747          */
27748         hide : true,
27749         /**
27750          * @event click
27751          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27752          * @param {Roo.bootstrap.menu.Menu} this
27753          * @param {Roo.EventObject} e
27754          */
27755         click : true
27756     });
27757     
27758 };
27759
27760 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27761     
27762     submenu : false,
27763     html : '',
27764     weight : 'default',
27765     icon : false,
27766     pos : 'bottom',
27767     
27768     
27769     getChildContainer : function() {
27770         if(this.isSubMenu){
27771             return this.el;
27772         }
27773         
27774         return this.el.select('ul.dropdown-menu', true).first();  
27775     },
27776     
27777     getAutoCreate : function()
27778     {
27779         var text = [
27780             {
27781                 tag : 'span',
27782                 cls : 'roo-menu-text',
27783                 html : this.html
27784             }
27785         ];
27786         
27787         if(this.icon){
27788             text.unshift({
27789                 tag : 'i',
27790                 cls : 'fa ' + this.icon
27791             })
27792         }
27793         
27794         
27795         var cfg = {
27796             tag : 'div',
27797             cls : 'btn-group',
27798             cn : [
27799                 {
27800                     tag : 'button',
27801                     cls : 'dropdown-button btn btn-' + this.weight,
27802                     cn : text
27803                 },
27804                 {
27805                     tag : 'button',
27806                     cls : 'dropdown-toggle btn btn-' + this.weight,
27807                     cn : [
27808                         {
27809                             tag : 'span',
27810                             cls : 'caret'
27811                         }
27812                     ]
27813                 },
27814                 {
27815                     tag : 'ul',
27816                     cls : 'dropdown-menu'
27817                 }
27818             ]
27819             
27820         };
27821         
27822         if(this.pos == 'top'){
27823             cfg.cls += ' dropup';
27824         }
27825         
27826         if(this.isSubMenu){
27827             cfg = {
27828                 tag : 'ul',
27829                 cls : 'dropdown-menu'
27830             }
27831         }
27832         
27833         return cfg;
27834     },
27835     
27836     onRender : function(ct, position)
27837     {
27838         this.isSubMenu = ct.hasClass('dropdown-submenu');
27839         
27840         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27841     },
27842     
27843     initEvents : function() 
27844     {
27845         if(this.isSubMenu){
27846             return;
27847         }
27848         
27849         this.hidden = true;
27850         
27851         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27852         this.triggerEl.on('click', this.onTriggerPress, this);
27853         
27854         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27855         this.buttonEl.on('click', this.onClick, this);
27856         
27857     },
27858     
27859     list : function()
27860     {
27861         if(this.isSubMenu){
27862             return this.el;
27863         }
27864         
27865         return this.el.select('ul.dropdown-menu', true).first();
27866     },
27867     
27868     onClick : function(e)
27869     {
27870         this.fireEvent("click", this, e);
27871     },
27872     
27873     onTriggerPress  : function(e)
27874     {   
27875         if (this.isVisible()) {
27876             this.hide();
27877         } else {
27878             this.show();
27879         }
27880     },
27881     
27882     isVisible : function(){
27883         return !this.hidden;
27884     },
27885     
27886     show : function()
27887     {
27888         this.fireEvent("beforeshow", this);
27889         
27890         this.hidden = false;
27891         this.el.addClass('open');
27892         
27893         Roo.get(document).on("mouseup", this.onMouseUp, this);
27894         
27895         this.fireEvent("show", this);
27896         
27897         
27898     },
27899     
27900     hide : function()
27901     {
27902         this.fireEvent("beforehide", this);
27903         
27904         this.hidden = true;
27905         this.el.removeClass('open');
27906         
27907         Roo.get(document).un("mouseup", this.onMouseUp);
27908         
27909         this.fireEvent("hide", this);
27910     },
27911     
27912     onMouseUp : function()
27913     {
27914         this.hide();
27915     }
27916     
27917 });
27918
27919  
27920  /*
27921  * - LGPL
27922  *
27923  * menu item
27924  * 
27925  */
27926 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27927
27928 /**
27929  * @class Roo.bootstrap.menu.Item
27930  * @extends Roo.bootstrap.Component
27931  * Bootstrap MenuItem class
27932  * @cfg {Boolean} submenu (true | false) default false
27933  * @cfg {String} html text of the item
27934  * @cfg {String} href the link
27935  * @cfg {Boolean} disable (true | false) default false
27936  * @cfg {Boolean} preventDefault (true | false) default true
27937  * @cfg {String} icon Font awesome icon
27938  * @cfg {String} pos Submenu align to (left | right) default right 
27939  * 
27940  * 
27941  * @constructor
27942  * Create a new Item
27943  * @param {Object} config The config object
27944  */
27945
27946
27947 Roo.bootstrap.menu.Item = function(config){
27948     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27949     this.addEvents({
27950         /**
27951          * @event mouseover
27952          * Fires when the mouse is hovering over this menu
27953          * @param {Roo.bootstrap.menu.Item} this
27954          * @param {Roo.EventObject} e
27955          */
27956         mouseover : true,
27957         /**
27958          * @event mouseout
27959          * Fires when the mouse exits this menu
27960          * @param {Roo.bootstrap.menu.Item} this
27961          * @param {Roo.EventObject} e
27962          */
27963         mouseout : true,
27964         // raw events
27965         /**
27966          * @event click
27967          * The raw click event for the entire grid.
27968          * @param {Roo.EventObject} e
27969          */
27970         click : true
27971     });
27972 };
27973
27974 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27975     
27976     submenu : false,
27977     href : '',
27978     html : '',
27979     preventDefault: true,
27980     disable : false,
27981     icon : false,
27982     pos : 'right',
27983     
27984     getAutoCreate : function()
27985     {
27986         var text = [
27987             {
27988                 tag : 'span',
27989                 cls : 'roo-menu-item-text',
27990                 html : this.html
27991             }
27992         ];
27993         
27994         if(this.icon){
27995             text.unshift({
27996                 tag : 'i',
27997                 cls : 'fa ' + this.icon
27998             })
27999         }
28000         
28001         var cfg = {
28002             tag : 'li',
28003             cn : [
28004                 {
28005                     tag : 'a',
28006                     href : this.href || '#',
28007                     cn : text
28008                 }
28009             ]
28010         };
28011         
28012         if(this.disable){
28013             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28014         }
28015         
28016         if(this.submenu){
28017             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28018             
28019             if(this.pos == 'left'){
28020                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28021             }
28022         }
28023         
28024         return cfg;
28025     },
28026     
28027     initEvents : function() 
28028     {
28029         this.el.on('mouseover', this.onMouseOver, this);
28030         this.el.on('mouseout', this.onMouseOut, this);
28031         
28032         this.el.select('a', true).first().on('click', this.onClick, this);
28033         
28034     },
28035     
28036     onClick : function(e)
28037     {
28038         if(this.preventDefault){
28039             e.preventDefault();
28040         }
28041         
28042         this.fireEvent("click", this, e);
28043     },
28044     
28045     onMouseOver : function(e)
28046     {
28047         if(this.submenu && this.pos == 'left'){
28048             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28049         }
28050         
28051         this.fireEvent("mouseover", this, e);
28052     },
28053     
28054     onMouseOut : function(e)
28055     {
28056         this.fireEvent("mouseout", this, e);
28057     }
28058 });
28059
28060  
28061
28062  /*
28063  * - LGPL
28064  *
28065  * menu separator
28066  * 
28067  */
28068 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28069
28070 /**
28071  * @class Roo.bootstrap.menu.Separator
28072  * @extends Roo.bootstrap.Component
28073  * Bootstrap Separator class
28074  * 
28075  * @constructor
28076  * Create a new Separator
28077  * @param {Object} config The config object
28078  */
28079
28080
28081 Roo.bootstrap.menu.Separator = function(config){
28082     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28083 };
28084
28085 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28086     
28087     getAutoCreate : function(){
28088         var cfg = {
28089             tag : 'li',
28090             cls: 'divider'
28091         };
28092         
28093         return cfg;
28094     }
28095    
28096 });
28097
28098  
28099
28100  /*
28101  * - LGPL
28102  *
28103  * Tooltip
28104  * 
28105  */
28106
28107 /**
28108  * @class Roo.bootstrap.Tooltip
28109  * Bootstrap Tooltip class
28110  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28111  * to determine which dom element triggers the tooltip.
28112  * 
28113  * It needs to add support for additional attributes like tooltip-position
28114  * 
28115  * @constructor
28116  * Create a new Toolti
28117  * @param {Object} config The config object
28118  */
28119
28120 Roo.bootstrap.Tooltip = function(config){
28121     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28122     
28123     this.alignment = Roo.bootstrap.Tooltip.alignment;
28124     
28125     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28126         this.alignment = config.alignment;
28127     }
28128     
28129 };
28130
28131 Roo.apply(Roo.bootstrap.Tooltip, {
28132     /**
28133      * @function init initialize tooltip monitoring.
28134      * @static
28135      */
28136     currentEl : false,
28137     currentTip : false,
28138     currentRegion : false,
28139     
28140     //  init : delay?
28141     
28142     init : function()
28143     {
28144         Roo.get(document).on('mouseover', this.enter ,this);
28145         Roo.get(document).on('mouseout', this.leave, this);
28146          
28147         
28148         this.currentTip = new Roo.bootstrap.Tooltip();
28149     },
28150     
28151     enter : function(ev)
28152     {
28153         var dom = ev.getTarget();
28154         
28155         //Roo.log(['enter',dom]);
28156         var el = Roo.fly(dom);
28157         if (this.currentEl) {
28158             //Roo.log(dom);
28159             //Roo.log(this.currentEl);
28160             //Roo.log(this.currentEl.contains(dom));
28161             if (this.currentEl == el) {
28162                 return;
28163             }
28164             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28165                 return;
28166             }
28167
28168         }
28169         
28170         if (this.currentTip.el) {
28171             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28172         }    
28173         //Roo.log(ev);
28174         
28175         if(!el || el.dom == document){
28176             return;
28177         }
28178         
28179         var bindEl = el;
28180         
28181         // you can not look for children, as if el is the body.. then everythign is the child..
28182         if (!el.attr('tooltip')) { //
28183             if (!el.select("[tooltip]").elements.length) {
28184                 return;
28185             }
28186             // is the mouse over this child...?
28187             bindEl = el.select("[tooltip]").first();
28188             var xy = ev.getXY();
28189             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28190                 //Roo.log("not in region.");
28191                 return;
28192             }
28193             //Roo.log("child element over..");
28194             
28195         }
28196         this.currentEl = bindEl;
28197         this.currentTip.bind(bindEl);
28198         this.currentRegion = Roo.lib.Region.getRegion(dom);
28199         this.currentTip.enter();
28200         
28201     },
28202     leave : function(ev)
28203     {
28204         var dom = ev.getTarget();
28205         //Roo.log(['leave',dom]);
28206         if (!this.currentEl) {
28207             return;
28208         }
28209         
28210         
28211         if (dom != this.currentEl.dom) {
28212             return;
28213         }
28214         var xy = ev.getXY();
28215         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28216             return;
28217         }
28218         // only activate leave if mouse cursor is outside... bounding box..
28219         
28220         
28221         
28222         
28223         if (this.currentTip) {
28224             this.currentTip.leave();
28225         }
28226         //Roo.log('clear currentEl');
28227         this.currentEl = false;
28228         
28229         
28230     },
28231     alignment : {
28232         'left' : ['r-l', [-2,0], 'right'],
28233         'right' : ['l-r', [2,0], 'left'],
28234         'bottom' : ['t-b', [0,2], 'top'],
28235         'top' : [ 'b-t', [0,-2], 'bottom']
28236     }
28237     
28238 });
28239
28240
28241 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28242     
28243     
28244     bindEl : false,
28245     
28246     delay : null, // can be { show : 300 , hide: 500}
28247     
28248     timeout : null,
28249     
28250     hoverState : null, //???
28251     
28252     placement : 'bottom', 
28253     
28254     alignment : false,
28255     
28256     getAutoCreate : function(){
28257     
28258         var cfg = {
28259            cls : 'tooltip',
28260            role : 'tooltip',
28261            cn : [
28262                 {
28263                     cls : 'tooltip-arrow'
28264                 },
28265                 {
28266                     cls : 'tooltip-inner'
28267                 }
28268            ]
28269         };
28270         
28271         return cfg;
28272     },
28273     bind : function(el)
28274     {
28275         this.bindEl = el;
28276     },
28277       
28278     
28279     enter : function () {
28280        
28281         if (this.timeout != null) {
28282             clearTimeout(this.timeout);
28283         }
28284         
28285         this.hoverState = 'in';
28286          //Roo.log("enter - show");
28287         if (!this.delay || !this.delay.show) {
28288             this.show();
28289             return;
28290         }
28291         var _t = this;
28292         this.timeout = setTimeout(function () {
28293             if (_t.hoverState == 'in') {
28294                 _t.show();
28295             }
28296         }, this.delay.show);
28297     },
28298     leave : function()
28299     {
28300         clearTimeout(this.timeout);
28301     
28302         this.hoverState = 'out';
28303          if (!this.delay || !this.delay.hide) {
28304             this.hide();
28305             return;
28306         }
28307        
28308         var _t = this;
28309         this.timeout = setTimeout(function () {
28310             //Roo.log("leave - timeout");
28311             
28312             if (_t.hoverState == 'out') {
28313                 _t.hide();
28314                 Roo.bootstrap.Tooltip.currentEl = false;
28315             }
28316         }, delay);
28317     },
28318     
28319     show : function (msg)
28320     {
28321         if (!this.el) {
28322             this.render(document.body);
28323         }
28324         // set content.
28325         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28326         
28327         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28328         
28329         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28330         
28331         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
28332         
28333         var placement = typeof this.placement == 'function' ?
28334             this.placement.call(this, this.el, on_el) :
28335             this.placement;
28336             
28337         var autoToken = /\s?auto?\s?/i;
28338         var autoPlace = autoToken.test(placement);
28339         if (autoPlace) {
28340             placement = placement.replace(autoToken, '') || 'top';
28341         }
28342         
28343         //this.el.detach()
28344         //this.el.setXY([0,0]);
28345         this.el.show();
28346         //this.el.dom.style.display='block';
28347         
28348         //this.el.appendTo(on_el);
28349         
28350         var p = this.getPosition();
28351         var box = this.el.getBox();
28352         
28353         if (autoPlace) {
28354             // fixme..
28355         }
28356         
28357         var align = this.alignment[placement];
28358         
28359         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28360         
28361         if(placement == 'top' || placement == 'bottom'){
28362             if(xy[0] < 0){
28363                 placement = 'right';
28364             }
28365             
28366             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28367                 placement = 'left';
28368             }
28369             
28370             var scroll = Roo.select('body', true).first().getScroll();
28371             
28372             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28373                 placement = 'top';
28374             }
28375             
28376             align = this.alignment[placement];
28377         }
28378         
28379         this.el.alignTo(this.bindEl, align[0],align[1]);
28380         //var arrow = this.el.select('.arrow',true).first();
28381         //arrow.set(align[2], 
28382         
28383         this.el.addClass(placement);
28384         
28385         this.el.addClass('in fade');
28386         
28387         this.hoverState = null;
28388         
28389         if (this.el.hasClass('fade')) {
28390             // fade it?
28391         }
28392         
28393     },
28394     hide : function()
28395     {
28396          
28397         if (!this.el) {
28398             return;
28399         }
28400         //this.el.setXY([0,0]);
28401         this.el.removeClass('in');
28402         //this.el.hide();
28403         
28404     }
28405     
28406 });
28407  
28408
28409  /*
28410  * - LGPL
28411  *
28412  * Location Picker
28413  * 
28414  */
28415
28416 /**
28417  * @class Roo.bootstrap.LocationPicker
28418  * @extends Roo.bootstrap.Component
28419  * Bootstrap LocationPicker class
28420  * @cfg {Number} latitude Position when init default 0
28421  * @cfg {Number} longitude Position when init default 0
28422  * @cfg {Number} zoom default 15
28423  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28424  * @cfg {Boolean} mapTypeControl default false
28425  * @cfg {Boolean} disableDoubleClickZoom default false
28426  * @cfg {Boolean} scrollwheel default true
28427  * @cfg {Boolean} streetViewControl default false
28428  * @cfg {Number} radius default 0
28429  * @cfg {String} locationName
28430  * @cfg {Boolean} draggable default true
28431  * @cfg {Boolean} enableAutocomplete default false
28432  * @cfg {Boolean} enableReverseGeocode default true
28433  * @cfg {String} markerTitle
28434  * 
28435  * @constructor
28436  * Create a new LocationPicker
28437  * @param {Object} config The config object
28438  */
28439
28440
28441 Roo.bootstrap.LocationPicker = function(config){
28442     
28443     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28444     
28445     this.addEvents({
28446         /**
28447          * @event initial
28448          * Fires when the picker initialized.
28449          * @param {Roo.bootstrap.LocationPicker} this
28450          * @param {Google Location} location
28451          */
28452         initial : true,
28453         /**
28454          * @event positionchanged
28455          * Fires when the picker position changed.
28456          * @param {Roo.bootstrap.LocationPicker} this
28457          * @param {Google Location} location
28458          */
28459         positionchanged : true,
28460         /**
28461          * @event resize
28462          * Fires when the map resize.
28463          * @param {Roo.bootstrap.LocationPicker} this
28464          */
28465         resize : true,
28466         /**
28467          * @event show
28468          * Fires when the map show.
28469          * @param {Roo.bootstrap.LocationPicker} this
28470          */
28471         show : true,
28472         /**
28473          * @event hide
28474          * Fires when the map hide.
28475          * @param {Roo.bootstrap.LocationPicker} this
28476          */
28477         hide : true,
28478         /**
28479          * @event mapClick
28480          * Fires when click the map.
28481          * @param {Roo.bootstrap.LocationPicker} this
28482          * @param {Map event} e
28483          */
28484         mapClick : true,
28485         /**
28486          * @event mapRightClick
28487          * Fires when right click the map.
28488          * @param {Roo.bootstrap.LocationPicker} this
28489          * @param {Map event} e
28490          */
28491         mapRightClick : true,
28492         /**
28493          * @event markerClick
28494          * Fires when click the marker.
28495          * @param {Roo.bootstrap.LocationPicker} this
28496          * @param {Map event} e
28497          */
28498         markerClick : true,
28499         /**
28500          * @event markerRightClick
28501          * Fires when right click the marker.
28502          * @param {Roo.bootstrap.LocationPicker} this
28503          * @param {Map event} e
28504          */
28505         markerRightClick : true,
28506         /**
28507          * @event OverlayViewDraw
28508          * Fires when OverlayView Draw
28509          * @param {Roo.bootstrap.LocationPicker} this
28510          */
28511         OverlayViewDraw : true,
28512         /**
28513          * @event OverlayViewOnAdd
28514          * Fires when OverlayView Draw
28515          * @param {Roo.bootstrap.LocationPicker} this
28516          */
28517         OverlayViewOnAdd : true,
28518         /**
28519          * @event OverlayViewOnRemove
28520          * Fires when OverlayView Draw
28521          * @param {Roo.bootstrap.LocationPicker} this
28522          */
28523         OverlayViewOnRemove : true,
28524         /**
28525          * @event OverlayViewShow
28526          * Fires when OverlayView Draw
28527          * @param {Roo.bootstrap.LocationPicker} this
28528          * @param {Pixel} cpx
28529          */
28530         OverlayViewShow : true,
28531         /**
28532          * @event OverlayViewHide
28533          * Fires when OverlayView Draw
28534          * @param {Roo.bootstrap.LocationPicker} this
28535          */
28536         OverlayViewHide : true,
28537         /**
28538          * @event loadexception
28539          * Fires when load google lib failed.
28540          * @param {Roo.bootstrap.LocationPicker} this
28541          */
28542         loadexception : true
28543     });
28544         
28545 };
28546
28547 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28548     
28549     gMapContext: false,
28550     
28551     latitude: 0,
28552     longitude: 0,
28553     zoom: 15,
28554     mapTypeId: false,
28555     mapTypeControl: false,
28556     disableDoubleClickZoom: false,
28557     scrollwheel: true,
28558     streetViewControl: false,
28559     radius: 0,
28560     locationName: '',
28561     draggable: true,
28562     enableAutocomplete: false,
28563     enableReverseGeocode: true,
28564     markerTitle: '',
28565     
28566     getAutoCreate: function()
28567     {
28568
28569         var cfg = {
28570             tag: 'div',
28571             cls: 'roo-location-picker'
28572         };
28573         
28574         return cfg
28575     },
28576     
28577     initEvents: function(ct, position)
28578     {       
28579         if(!this.el.getWidth() || this.isApplied()){
28580             return;
28581         }
28582         
28583         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28584         
28585         this.initial();
28586     },
28587     
28588     initial: function()
28589     {
28590         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28591             this.fireEvent('loadexception', this);
28592             return;
28593         }
28594         
28595         if(!this.mapTypeId){
28596             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28597         }
28598         
28599         this.gMapContext = this.GMapContext();
28600         
28601         this.initOverlayView();
28602         
28603         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28604         
28605         var _this = this;
28606                 
28607         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28608             _this.setPosition(_this.gMapContext.marker.position);
28609         });
28610         
28611         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28612             _this.fireEvent('mapClick', this, event);
28613             
28614         });
28615
28616         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28617             _this.fireEvent('mapRightClick', this, event);
28618             
28619         });
28620         
28621         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28622             _this.fireEvent('markerClick', this, event);
28623             
28624         });
28625
28626         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28627             _this.fireEvent('markerRightClick', this, event);
28628             
28629         });
28630         
28631         this.setPosition(this.gMapContext.location);
28632         
28633         this.fireEvent('initial', this, this.gMapContext.location);
28634     },
28635     
28636     initOverlayView: function()
28637     {
28638         var _this = this;
28639         
28640         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28641             
28642             draw: function()
28643             {
28644                 _this.fireEvent('OverlayViewDraw', _this);
28645             },
28646             
28647             onAdd: function()
28648             {
28649                 _this.fireEvent('OverlayViewOnAdd', _this);
28650             },
28651             
28652             onRemove: function()
28653             {
28654                 _this.fireEvent('OverlayViewOnRemove', _this);
28655             },
28656             
28657             show: function(cpx)
28658             {
28659                 _this.fireEvent('OverlayViewShow', _this, cpx);
28660             },
28661             
28662             hide: function()
28663             {
28664                 _this.fireEvent('OverlayViewHide', _this);
28665             }
28666             
28667         });
28668     },
28669     
28670     fromLatLngToContainerPixel: function(event)
28671     {
28672         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28673     },
28674     
28675     isApplied: function() 
28676     {
28677         return this.getGmapContext() == false ? false : true;
28678     },
28679     
28680     getGmapContext: function() 
28681     {
28682         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28683     },
28684     
28685     GMapContext: function() 
28686     {
28687         var position = new google.maps.LatLng(this.latitude, this.longitude);
28688         
28689         var _map = new google.maps.Map(this.el.dom, {
28690             center: position,
28691             zoom: this.zoom,
28692             mapTypeId: this.mapTypeId,
28693             mapTypeControl: this.mapTypeControl,
28694             disableDoubleClickZoom: this.disableDoubleClickZoom,
28695             scrollwheel: this.scrollwheel,
28696             streetViewControl: this.streetViewControl,
28697             locationName: this.locationName,
28698             draggable: this.draggable,
28699             enableAutocomplete: this.enableAutocomplete,
28700             enableReverseGeocode: this.enableReverseGeocode
28701         });
28702         
28703         var _marker = new google.maps.Marker({
28704             position: position,
28705             map: _map,
28706             title: this.markerTitle,
28707             draggable: this.draggable
28708         });
28709         
28710         return {
28711             map: _map,
28712             marker: _marker,
28713             circle: null,
28714             location: position,
28715             radius: this.radius,
28716             locationName: this.locationName,
28717             addressComponents: {
28718                 formatted_address: null,
28719                 addressLine1: null,
28720                 addressLine2: null,
28721                 streetName: null,
28722                 streetNumber: null,
28723                 city: null,
28724                 district: null,
28725                 state: null,
28726                 stateOrProvince: null
28727             },
28728             settings: this,
28729             domContainer: this.el.dom,
28730             geodecoder: new google.maps.Geocoder()
28731         };
28732     },
28733     
28734     drawCircle: function(center, radius, options) 
28735     {
28736         if (this.gMapContext.circle != null) {
28737             this.gMapContext.circle.setMap(null);
28738         }
28739         if (radius > 0) {
28740             radius *= 1;
28741             options = Roo.apply({}, options, {
28742                 strokeColor: "#0000FF",
28743                 strokeOpacity: .35,
28744                 strokeWeight: 2,
28745                 fillColor: "#0000FF",
28746                 fillOpacity: .2
28747             });
28748             
28749             options.map = this.gMapContext.map;
28750             options.radius = radius;
28751             options.center = center;
28752             this.gMapContext.circle = new google.maps.Circle(options);
28753             return this.gMapContext.circle;
28754         }
28755         
28756         return null;
28757     },
28758     
28759     setPosition: function(location) 
28760     {
28761         this.gMapContext.location = location;
28762         this.gMapContext.marker.setPosition(location);
28763         this.gMapContext.map.panTo(location);
28764         this.drawCircle(location, this.gMapContext.radius, {});
28765         
28766         var _this = this;
28767         
28768         if (this.gMapContext.settings.enableReverseGeocode) {
28769             this.gMapContext.geodecoder.geocode({
28770                 latLng: this.gMapContext.location
28771             }, function(results, status) {
28772                 
28773                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28774                     _this.gMapContext.locationName = results[0].formatted_address;
28775                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28776                     
28777                     _this.fireEvent('positionchanged', this, location);
28778                 }
28779             });
28780             
28781             return;
28782         }
28783         
28784         this.fireEvent('positionchanged', this, location);
28785     },
28786     
28787     resize: function()
28788     {
28789         google.maps.event.trigger(this.gMapContext.map, "resize");
28790         
28791         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28792         
28793         this.fireEvent('resize', this);
28794     },
28795     
28796     setPositionByLatLng: function(latitude, longitude)
28797     {
28798         this.setPosition(new google.maps.LatLng(latitude, longitude));
28799     },
28800     
28801     getCurrentPosition: function() 
28802     {
28803         return {
28804             latitude: this.gMapContext.location.lat(),
28805             longitude: this.gMapContext.location.lng()
28806         };
28807     },
28808     
28809     getAddressName: function() 
28810     {
28811         return this.gMapContext.locationName;
28812     },
28813     
28814     getAddressComponents: function() 
28815     {
28816         return this.gMapContext.addressComponents;
28817     },
28818     
28819     address_component_from_google_geocode: function(address_components) 
28820     {
28821         var result = {};
28822         
28823         for (var i = 0; i < address_components.length; i++) {
28824             var component = address_components[i];
28825             if (component.types.indexOf("postal_code") >= 0) {
28826                 result.postalCode = component.short_name;
28827             } else if (component.types.indexOf("street_number") >= 0) {
28828                 result.streetNumber = component.short_name;
28829             } else if (component.types.indexOf("route") >= 0) {
28830                 result.streetName = component.short_name;
28831             } else if (component.types.indexOf("neighborhood") >= 0) {
28832                 result.city = component.short_name;
28833             } else if (component.types.indexOf("locality") >= 0) {
28834                 result.city = component.short_name;
28835             } else if (component.types.indexOf("sublocality") >= 0) {
28836                 result.district = component.short_name;
28837             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28838                 result.stateOrProvince = component.short_name;
28839             } else if (component.types.indexOf("country") >= 0) {
28840                 result.country = component.short_name;
28841             }
28842         }
28843         
28844         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28845         result.addressLine2 = "";
28846         return result;
28847     },
28848     
28849     setZoomLevel: function(zoom)
28850     {
28851         this.gMapContext.map.setZoom(zoom);
28852     },
28853     
28854     show: function()
28855     {
28856         if(!this.el){
28857             return;
28858         }
28859         
28860         this.el.show();
28861         
28862         this.resize();
28863         
28864         this.fireEvent('show', this);
28865     },
28866     
28867     hide: function()
28868     {
28869         if(!this.el){
28870             return;
28871         }
28872         
28873         this.el.hide();
28874         
28875         this.fireEvent('hide', this);
28876     }
28877     
28878 });
28879
28880 Roo.apply(Roo.bootstrap.LocationPicker, {
28881     
28882     OverlayView : function(map, options)
28883     {
28884         options = options || {};
28885         
28886         this.setMap(map);
28887     }
28888     
28889     
28890 });/**
28891  * @class Roo.bootstrap.Alert
28892  * @extends Roo.bootstrap.Component
28893  * Bootstrap Alert class - shows an alert area box
28894  * eg
28895  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28896   Enter a valid email address
28897 </div>
28898  * @licence LGPL
28899  * @cfg {String} title The title of alert
28900  * @cfg {String} html The content of alert
28901  * @cfg {String} weight (  success | info | warning | danger )
28902  * @cfg {String} faicon font-awesomeicon
28903  * 
28904  * @constructor
28905  * Create a new alert
28906  * @param {Object} config The config object
28907  */
28908
28909
28910 Roo.bootstrap.Alert = function(config){
28911     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28912     
28913 };
28914
28915 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28916     
28917     title: '',
28918     html: '',
28919     weight: false,
28920     faicon: false,
28921     
28922     getAutoCreate : function()
28923     {
28924         
28925         var cfg = {
28926             tag : 'div',
28927             cls : 'alert',
28928             cn : [
28929                 {
28930                     tag : 'i',
28931                     cls : 'roo-alert-icon'
28932                     
28933                 },
28934                 {
28935                     tag : 'b',
28936                     cls : 'roo-alert-title',
28937                     html : this.title
28938                 },
28939                 {
28940                     tag : 'span',
28941                     cls : 'roo-alert-text',
28942                     html : this.html
28943                 }
28944             ]
28945         };
28946         
28947         if(this.faicon){
28948             cfg.cn[0].cls += ' fa ' + this.faicon;
28949         }
28950         
28951         if(this.weight){
28952             cfg.cls += ' alert-' + this.weight;
28953         }
28954         
28955         return cfg;
28956     },
28957     
28958     initEvents: function() 
28959     {
28960         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28961     },
28962     
28963     setTitle : function(str)
28964     {
28965         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28966     },
28967     
28968     setText : function(str)
28969     {
28970         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28971     },
28972     
28973     setWeight : function(weight)
28974     {
28975         if(this.weight){
28976             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28977         }
28978         
28979         this.weight = weight;
28980         
28981         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
28982     },
28983     
28984     setIcon : function(icon)
28985     {
28986         if(this.faicon){
28987             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
28988         }
28989         
28990         this.faicon = icon;
28991         
28992         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
28993     },
28994     
28995     hide: function() 
28996     {
28997         this.el.hide();   
28998     },
28999     
29000     show: function() 
29001     {  
29002         this.el.show();   
29003     }
29004     
29005 });
29006
29007  
29008 /*
29009 * Licence: LGPL
29010 */
29011
29012 /**
29013  * @class Roo.bootstrap.UploadCropbox
29014  * @extends Roo.bootstrap.Component
29015  * Bootstrap UploadCropbox class
29016  * @cfg {String} emptyText show when image has been loaded
29017  * @cfg {String} rotateNotify show when image too small to rotate
29018  * @cfg {Number} errorTimeout default 3000
29019  * @cfg {Number} minWidth default 300
29020  * @cfg {Number} minHeight default 300
29021  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29022  * @cfg {Boolean} isDocument (true|false) default false
29023  * @cfg {String} url action url
29024  * @cfg {String} paramName default 'imageUpload'
29025  * @cfg {String} method default POST
29026  * @cfg {Boolean} loadMask (true|false) default true
29027  * @cfg {Boolean} loadingText default 'Loading...'
29028  * 
29029  * @constructor
29030  * Create a new UploadCropbox
29031  * @param {Object} config The config object
29032  */
29033
29034 Roo.bootstrap.UploadCropbox = function(config){
29035     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29036     
29037     this.addEvents({
29038         /**
29039          * @event beforeselectfile
29040          * Fire before select file
29041          * @param {Roo.bootstrap.UploadCropbox} this
29042          */
29043         "beforeselectfile" : true,
29044         /**
29045          * @event initial
29046          * Fire after initEvent
29047          * @param {Roo.bootstrap.UploadCropbox} this
29048          */
29049         "initial" : true,
29050         /**
29051          * @event crop
29052          * Fire after initEvent
29053          * @param {Roo.bootstrap.UploadCropbox} this
29054          * @param {String} data
29055          */
29056         "crop" : true,
29057         /**
29058          * @event prepare
29059          * Fire when preparing the file data
29060          * @param {Roo.bootstrap.UploadCropbox} this
29061          * @param {Object} file
29062          */
29063         "prepare" : true,
29064         /**
29065          * @event exception
29066          * Fire when get exception
29067          * @param {Roo.bootstrap.UploadCropbox} this
29068          * @param {XMLHttpRequest} xhr
29069          */
29070         "exception" : true,
29071         /**
29072          * @event beforeloadcanvas
29073          * Fire before load the canvas
29074          * @param {Roo.bootstrap.UploadCropbox} this
29075          * @param {String} src
29076          */
29077         "beforeloadcanvas" : true,
29078         /**
29079          * @event trash
29080          * Fire when trash image
29081          * @param {Roo.bootstrap.UploadCropbox} this
29082          */
29083         "trash" : true,
29084         /**
29085          * @event download
29086          * Fire when download the image
29087          * @param {Roo.bootstrap.UploadCropbox} this
29088          */
29089         "download" : true,
29090         /**
29091          * @event footerbuttonclick
29092          * Fire when footerbuttonclick
29093          * @param {Roo.bootstrap.UploadCropbox} this
29094          * @param {String} type
29095          */
29096         "footerbuttonclick" : true,
29097         /**
29098          * @event resize
29099          * Fire when resize
29100          * @param {Roo.bootstrap.UploadCropbox} this
29101          */
29102         "resize" : true,
29103         /**
29104          * @event rotate
29105          * Fire when rotate the image
29106          * @param {Roo.bootstrap.UploadCropbox} this
29107          * @param {String} pos
29108          */
29109         "rotate" : true,
29110         /**
29111          * @event inspect
29112          * Fire when inspect the file
29113          * @param {Roo.bootstrap.UploadCropbox} this
29114          * @param {Object} file
29115          */
29116         "inspect" : true,
29117         /**
29118          * @event upload
29119          * Fire when xhr upload the file
29120          * @param {Roo.bootstrap.UploadCropbox} this
29121          * @param {Object} data
29122          */
29123         "upload" : true,
29124         /**
29125          * @event arrange
29126          * Fire when arrange the file data
29127          * @param {Roo.bootstrap.UploadCropbox} this
29128          * @param {Object} formData
29129          */
29130         "arrange" : true
29131     });
29132     
29133     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29134 };
29135
29136 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29137     
29138     emptyText : 'Click to upload image',
29139     rotateNotify : 'Image is too small to rotate',
29140     errorTimeout : 3000,
29141     scale : 0,
29142     baseScale : 1,
29143     rotate : 0,
29144     dragable : false,
29145     pinching : false,
29146     mouseX : 0,
29147     mouseY : 0,
29148     cropData : false,
29149     minWidth : 300,
29150     minHeight : 300,
29151     file : false,
29152     exif : {},
29153     baseRotate : 1,
29154     cropType : 'image/jpeg',
29155     buttons : false,
29156     canvasLoaded : false,
29157     isDocument : false,
29158     method : 'POST',
29159     paramName : 'imageUpload',
29160     loadMask : true,
29161     loadingText : 'Loading...',
29162     maskEl : false,
29163     
29164     getAutoCreate : function()
29165     {
29166         var cfg = {
29167             tag : 'div',
29168             cls : 'roo-upload-cropbox',
29169             cn : [
29170                 {
29171                     tag : 'input',
29172                     cls : 'roo-upload-cropbox-selector',
29173                     type : 'file'
29174                 },
29175                 {
29176                     tag : 'div',
29177                     cls : 'roo-upload-cropbox-body',
29178                     style : 'cursor:pointer',
29179                     cn : [
29180                         {
29181                             tag : 'div',
29182                             cls : 'roo-upload-cropbox-preview'
29183                         },
29184                         {
29185                             tag : 'div',
29186                             cls : 'roo-upload-cropbox-thumb'
29187                         },
29188                         {
29189                             tag : 'div',
29190                             cls : 'roo-upload-cropbox-empty-notify',
29191                             html : this.emptyText
29192                         },
29193                         {
29194                             tag : 'div',
29195                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29196                             html : this.rotateNotify
29197                         }
29198                     ]
29199                 },
29200                 {
29201                     tag : 'div',
29202                     cls : 'roo-upload-cropbox-footer',
29203                     cn : {
29204                         tag : 'div',
29205                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29206                         cn : []
29207                     }
29208                 }
29209             ]
29210         };
29211         
29212         return cfg;
29213     },
29214     
29215     onRender : function(ct, position)
29216     {
29217         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29218         
29219         if (this.buttons.length) {
29220             
29221             Roo.each(this.buttons, function(bb) {
29222                 
29223                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29224                 
29225                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29226                 
29227             }, this);
29228         }
29229         
29230         if(this.loadMask){
29231             this.maskEl = this.el;
29232         }
29233     },
29234     
29235     initEvents : function()
29236     {
29237         this.urlAPI = (window.createObjectURL && window) || 
29238                                 (window.URL && URL.revokeObjectURL && URL) || 
29239                                 (window.webkitURL && webkitURL);
29240                         
29241         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29242         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29243         
29244         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29245         this.selectorEl.hide();
29246         
29247         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29248         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29249         
29250         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29251         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29252         this.thumbEl.hide();
29253         
29254         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29255         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29256         
29257         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29258         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29259         this.errorEl.hide();
29260         
29261         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29262         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29263         this.footerEl.hide();
29264         
29265         this.setThumbBoxSize();
29266         
29267         this.bind();
29268         
29269         this.resize();
29270         
29271         this.fireEvent('initial', this);
29272     },
29273
29274     bind : function()
29275     {
29276         var _this = this;
29277         
29278         window.addEventListener("resize", function() { _this.resize(); } );
29279         
29280         this.bodyEl.on('click', this.beforeSelectFile, this);
29281         
29282         if(Roo.isTouch){
29283             this.bodyEl.on('touchstart', this.onTouchStart, this);
29284             this.bodyEl.on('touchmove', this.onTouchMove, this);
29285             this.bodyEl.on('touchend', this.onTouchEnd, this);
29286         }
29287         
29288         if(!Roo.isTouch){
29289             this.bodyEl.on('mousedown', this.onMouseDown, this);
29290             this.bodyEl.on('mousemove', this.onMouseMove, this);
29291             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29292             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29293             Roo.get(document).on('mouseup', this.onMouseUp, this);
29294         }
29295         
29296         this.selectorEl.on('change', this.onFileSelected, this);
29297     },
29298     
29299     reset : function()
29300     {    
29301         this.scale = 0;
29302         this.baseScale = 1;
29303         this.rotate = 0;
29304         this.baseRotate = 1;
29305         this.dragable = false;
29306         this.pinching = false;
29307         this.mouseX = 0;
29308         this.mouseY = 0;
29309         this.cropData = false;
29310         this.notifyEl.dom.innerHTML = this.emptyText;
29311         
29312         this.selectorEl.dom.value = '';
29313         
29314     },
29315     
29316     resize : function()
29317     {
29318         if(this.fireEvent('resize', this) != false){
29319             this.setThumbBoxPosition();
29320             this.setCanvasPosition();
29321         }
29322     },
29323     
29324     onFooterButtonClick : function(e, el, o, type)
29325     {
29326         switch (type) {
29327             case 'rotate-left' :
29328                 this.onRotateLeft(e);
29329                 break;
29330             case 'rotate-right' :
29331                 this.onRotateRight(e);
29332                 break;
29333             case 'picture' :
29334                 this.beforeSelectFile(e);
29335                 break;
29336             case 'trash' :
29337                 this.trash(e);
29338                 break;
29339             case 'crop' :
29340                 this.crop(e);
29341                 break;
29342             case 'download' :
29343                 this.download(e);
29344                 break;
29345             default :
29346                 break;
29347         }
29348         
29349         this.fireEvent('footerbuttonclick', this, type);
29350     },
29351     
29352     beforeSelectFile : function(e)
29353     {
29354         e.preventDefault();
29355         
29356         if(this.fireEvent('beforeselectfile', this) != false){
29357             this.selectorEl.dom.click();
29358         }
29359     },
29360     
29361     onFileSelected : function(e)
29362     {
29363         e.preventDefault();
29364         
29365         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29366             return;
29367         }
29368         
29369         var file = this.selectorEl.dom.files[0];
29370         
29371         if(this.fireEvent('inspect', this, file) != false){
29372             this.prepare(file);
29373         }
29374         
29375     },
29376     
29377     trash : function(e)
29378     {
29379         this.fireEvent('trash', this);
29380     },
29381     
29382     download : function(e)
29383     {
29384         this.fireEvent('download', this);
29385     },
29386     
29387     loadCanvas : function(src)
29388     {   
29389         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29390             
29391             this.reset();
29392             
29393             this.imageEl = document.createElement('img');
29394             
29395             var _this = this;
29396             
29397             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29398             
29399             this.imageEl.src = src;
29400         }
29401     },
29402     
29403     onLoadCanvas : function()
29404     {   
29405         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29406         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29407         
29408         this.bodyEl.un('click', this.beforeSelectFile, this);
29409         
29410         this.notifyEl.hide();
29411         this.thumbEl.show();
29412         this.footerEl.show();
29413         
29414         this.baseRotateLevel();
29415         
29416         if(this.isDocument){
29417             this.setThumbBoxSize();
29418         }
29419         
29420         this.setThumbBoxPosition();
29421         
29422         this.baseScaleLevel();
29423         
29424         this.draw();
29425         
29426         this.resize();
29427         
29428         this.canvasLoaded = true;
29429         
29430         if(this.loadMask){
29431             this.maskEl.unmask();
29432         }
29433         
29434     },
29435     
29436     setCanvasPosition : function()
29437     {   
29438         if(!this.canvasEl){
29439             return;
29440         }
29441         
29442         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29443         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29444         
29445         this.previewEl.setLeft(pw);
29446         this.previewEl.setTop(ph);
29447         
29448     },
29449     
29450     onMouseDown : function(e)
29451     {   
29452         e.stopEvent();
29453         
29454         this.dragable = true;
29455         this.pinching = false;
29456         
29457         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29458             this.dragable = false;
29459             return;
29460         }
29461         
29462         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29463         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29464         
29465     },
29466     
29467     onMouseMove : function(e)
29468     {   
29469         e.stopEvent();
29470         
29471         if(!this.canvasLoaded){
29472             return;
29473         }
29474         
29475         if (!this.dragable){
29476             return;
29477         }
29478         
29479         var minX = Math.ceil(this.thumbEl.getLeft(true));
29480         var minY = Math.ceil(this.thumbEl.getTop(true));
29481         
29482         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29483         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29484         
29485         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29486         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29487         
29488         x = x - this.mouseX;
29489         y = y - this.mouseY;
29490         
29491         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29492         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29493         
29494         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29495         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29496         
29497         this.previewEl.setLeft(bgX);
29498         this.previewEl.setTop(bgY);
29499         
29500         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29501         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29502     },
29503     
29504     onMouseUp : function(e)
29505     {   
29506         e.stopEvent();
29507         
29508         this.dragable = false;
29509     },
29510     
29511     onMouseWheel : function(e)
29512     {   
29513         e.stopEvent();
29514         
29515         this.startScale = this.scale;
29516         
29517         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29518         
29519         if(!this.zoomable()){
29520             this.scale = this.startScale;
29521             return;
29522         }
29523         
29524         this.draw();
29525         
29526         return;
29527     },
29528     
29529     zoomable : function()
29530     {
29531         var minScale = this.thumbEl.getWidth() / this.minWidth;
29532         
29533         if(this.minWidth < this.minHeight){
29534             minScale = this.thumbEl.getHeight() / this.minHeight;
29535         }
29536         
29537         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29538         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29539         
29540         if(
29541                 this.isDocument &&
29542                 (this.rotate == 0 || this.rotate == 180) && 
29543                 (
29544                     width > this.imageEl.OriginWidth || 
29545                     height > this.imageEl.OriginHeight ||
29546                     (width < this.minWidth && height < this.minHeight)
29547                 )
29548         ){
29549             return false;
29550         }
29551         
29552         if(
29553                 this.isDocument &&
29554                 (this.rotate == 90 || this.rotate == 270) && 
29555                 (
29556                     width > this.imageEl.OriginWidth || 
29557                     height > this.imageEl.OriginHeight ||
29558                     (width < this.minHeight && height < this.minWidth)
29559                 )
29560         ){
29561             return false;
29562         }
29563         
29564         if(
29565                 !this.isDocument &&
29566                 (this.rotate == 0 || this.rotate == 180) && 
29567                 (
29568                     width < this.minWidth || 
29569                     width > this.imageEl.OriginWidth || 
29570                     height < this.minHeight || 
29571                     height > this.imageEl.OriginHeight
29572                 )
29573         ){
29574             return false;
29575         }
29576         
29577         if(
29578                 !this.isDocument &&
29579                 (this.rotate == 90 || this.rotate == 270) && 
29580                 (
29581                     width < this.minHeight || 
29582                     width > this.imageEl.OriginWidth || 
29583                     height < this.minWidth || 
29584                     height > this.imageEl.OriginHeight
29585                 )
29586         ){
29587             return false;
29588         }
29589         
29590         return true;
29591         
29592     },
29593     
29594     onRotateLeft : function(e)
29595     {   
29596         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29597             
29598             var minScale = this.thumbEl.getWidth() / this.minWidth;
29599             
29600             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29601             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29602             
29603             this.startScale = this.scale;
29604             
29605             while (this.getScaleLevel() < minScale){
29606             
29607                 this.scale = this.scale + 1;
29608                 
29609                 if(!this.zoomable()){
29610                     break;
29611                 }
29612                 
29613                 if(
29614                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29615                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29616                 ){
29617                     continue;
29618                 }
29619                 
29620                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29621
29622                 this.draw();
29623                 
29624                 return;
29625             }
29626             
29627             this.scale = this.startScale;
29628             
29629             this.onRotateFail();
29630             
29631             return false;
29632         }
29633         
29634         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29635
29636         if(this.isDocument){
29637             this.setThumbBoxSize();
29638             this.setThumbBoxPosition();
29639             this.setCanvasPosition();
29640         }
29641         
29642         this.draw();
29643         
29644         this.fireEvent('rotate', this, 'left');
29645         
29646     },
29647     
29648     onRotateRight : function(e)
29649     {
29650         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29651             
29652             var minScale = this.thumbEl.getWidth() / this.minWidth;
29653         
29654             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29655             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29656             
29657             this.startScale = this.scale;
29658             
29659             while (this.getScaleLevel() < minScale){
29660             
29661                 this.scale = this.scale + 1;
29662                 
29663                 if(!this.zoomable()){
29664                     break;
29665                 }
29666                 
29667                 if(
29668                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29669                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29670                 ){
29671                     continue;
29672                 }
29673                 
29674                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29675
29676                 this.draw();
29677                 
29678                 return;
29679             }
29680             
29681             this.scale = this.startScale;
29682             
29683             this.onRotateFail();
29684             
29685             return false;
29686         }
29687         
29688         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29689
29690         if(this.isDocument){
29691             this.setThumbBoxSize();
29692             this.setThumbBoxPosition();
29693             this.setCanvasPosition();
29694         }
29695         
29696         this.draw();
29697         
29698         this.fireEvent('rotate', this, 'right');
29699     },
29700     
29701     onRotateFail : function()
29702     {
29703         this.errorEl.show(true);
29704         
29705         var _this = this;
29706         
29707         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29708     },
29709     
29710     draw : function()
29711     {
29712         this.previewEl.dom.innerHTML = '';
29713         
29714         var canvasEl = document.createElement("canvas");
29715         
29716         var contextEl = canvasEl.getContext("2d");
29717         
29718         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29719         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29720         var center = this.imageEl.OriginWidth / 2;
29721         
29722         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29723             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29724             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29725             center = this.imageEl.OriginHeight / 2;
29726         }
29727         
29728         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29729         
29730         contextEl.translate(center, center);
29731         contextEl.rotate(this.rotate * Math.PI / 180);
29732
29733         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29734         
29735         this.canvasEl = document.createElement("canvas");
29736         
29737         this.contextEl = this.canvasEl.getContext("2d");
29738         
29739         switch (this.rotate) {
29740             case 0 :
29741                 
29742                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29743                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29744                 
29745                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29746                 
29747                 break;
29748             case 90 : 
29749                 
29750                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29751                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29752                 
29753                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29754                     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);
29755                     break;
29756                 }
29757                 
29758                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29759                 
29760                 break;
29761             case 180 :
29762                 
29763                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29764                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29765                 
29766                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29767                     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);
29768                     break;
29769                 }
29770                 
29771                 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);
29772                 
29773                 break;
29774             case 270 :
29775                 
29776                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29777                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29778         
29779                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29780                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29781                     break;
29782                 }
29783                 
29784                 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);
29785                 
29786                 break;
29787             default : 
29788                 break;
29789         }
29790         
29791         this.previewEl.appendChild(this.canvasEl);
29792         
29793         this.setCanvasPosition();
29794     },
29795     
29796     crop : function()
29797     {
29798         if(!this.canvasLoaded){
29799             return;
29800         }
29801         
29802         var imageCanvas = document.createElement("canvas");
29803         
29804         var imageContext = imageCanvas.getContext("2d");
29805         
29806         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29807         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29808         
29809         var center = imageCanvas.width / 2;
29810         
29811         imageContext.translate(center, center);
29812         
29813         imageContext.rotate(this.rotate * Math.PI / 180);
29814         
29815         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29816         
29817         var canvas = document.createElement("canvas");
29818         
29819         var context = canvas.getContext("2d");
29820                 
29821         canvas.width = this.minWidth;
29822         canvas.height = this.minHeight;
29823
29824         switch (this.rotate) {
29825             case 0 :
29826                 
29827                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29828                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29829                 
29830                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29831                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29832                 
29833                 var targetWidth = this.minWidth - 2 * x;
29834                 var targetHeight = this.minHeight - 2 * y;
29835                 
29836                 var scale = 1;
29837                 
29838                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29839                     scale = targetWidth / width;
29840                 }
29841                 
29842                 if(x > 0 && y == 0){
29843                     scale = targetHeight / height;
29844                 }
29845                 
29846                 if(x > 0 && y > 0){
29847                     scale = targetWidth / width;
29848                     
29849                     if(width < height){
29850                         scale = targetHeight / height;
29851                     }
29852                 }
29853                 
29854                 context.scale(scale, scale);
29855                 
29856                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29857                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29858
29859                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29860                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29861
29862                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29863                 
29864                 break;
29865             case 90 : 
29866                 
29867                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29868                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29869                 
29870                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29871                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29872                 
29873                 var targetWidth = this.minWidth - 2 * x;
29874                 var targetHeight = this.minHeight - 2 * y;
29875                 
29876                 var scale = 1;
29877                 
29878                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29879                     scale = targetWidth / width;
29880                 }
29881                 
29882                 if(x > 0 && y == 0){
29883                     scale = targetHeight / height;
29884                 }
29885                 
29886                 if(x > 0 && y > 0){
29887                     scale = targetWidth / width;
29888                     
29889                     if(width < height){
29890                         scale = targetHeight / height;
29891                     }
29892                 }
29893                 
29894                 context.scale(scale, scale);
29895                 
29896                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29897                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29898
29899                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29900                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29901                 
29902                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29903                 
29904                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29905                 
29906                 break;
29907             case 180 :
29908                 
29909                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29910                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29911                 
29912                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29913                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29914                 
29915                 var targetWidth = this.minWidth - 2 * x;
29916                 var targetHeight = this.minHeight - 2 * y;
29917                 
29918                 var scale = 1;
29919                 
29920                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29921                     scale = targetWidth / width;
29922                 }
29923                 
29924                 if(x > 0 && y == 0){
29925                     scale = targetHeight / height;
29926                 }
29927                 
29928                 if(x > 0 && y > 0){
29929                     scale = targetWidth / width;
29930                     
29931                     if(width < height){
29932                         scale = targetHeight / height;
29933                     }
29934                 }
29935                 
29936                 context.scale(scale, scale);
29937                 
29938                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29939                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29940
29941                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29942                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29943
29944                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29945                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29946                 
29947                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29948                 
29949                 break;
29950             case 270 :
29951                 
29952                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29953                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29954                 
29955                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29956                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29957                 
29958                 var targetWidth = this.minWidth - 2 * x;
29959                 var targetHeight = this.minHeight - 2 * y;
29960                 
29961                 var scale = 1;
29962                 
29963                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29964                     scale = targetWidth / width;
29965                 }
29966                 
29967                 if(x > 0 && y == 0){
29968                     scale = targetHeight / height;
29969                 }
29970                 
29971                 if(x > 0 && y > 0){
29972                     scale = targetWidth / width;
29973                     
29974                     if(width < height){
29975                         scale = targetHeight / height;
29976                     }
29977                 }
29978                 
29979                 context.scale(scale, scale);
29980                 
29981                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29982                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29983
29984                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29985                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29986                 
29987                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29988                 
29989                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29990                 
29991                 break;
29992             default : 
29993                 break;
29994         }
29995         
29996         this.cropData = canvas.toDataURL(this.cropType);
29997         
29998         if(this.fireEvent('crop', this, this.cropData) !== false){
29999             this.process(this.file, this.cropData);
30000         }
30001         
30002         return;
30003         
30004     },
30005     
30006     setThumbBoxSize : function()
30007     {
30008         var width, height;
30009         
30010         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30011             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30012             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30013             
30014             this.minWidth = width;
30015             this.minHeight = height;
30016             
30017             if(this.rotate == 90 || this.rotate == 270){
30018                 this.minWidth = height;
30019                 this.minHeight = width;
30020             }
30021         }
30022         
30023         height = 300;
30024         width = Math.ceil(this.minWidth * height / this.minHeight);
30025         
30026         if(this.minWidth > this.minHeight){
30027             width = 300;
30028             height = Math.ceil(this.minHeight * width / this.minWidth);
30029         }
30030         
30031         this.thumbEl.setStyle({
30032             width : width + 'px',
30033             height : height + 'px'
30034         });
30035
30036         return;
30037             
30038     },
30039     
30040     setThumbBoxPosition : function()
30041     {
30042         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30043         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30044         
30045         this.thumbEl.setLeft(x);
30046         this.thumbEl.setTop(y);
30047         
30048     },
30049     
30050     baseRotateLevel : function()
30051     {
30052         this.baseRotate = 1;
30053         
30054         if(
30055                 typeof(this.exif) != 'undefined' &&
30056                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30057                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30058         ){
30059             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30060         }
30061         
30062         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30063         
30064     },
30065     
30066     baseScaleLevel : function()
30067     {
30068         var width, height;
30069         
30070         if(this.isDocument){
30071             
30072             if(this.baseRotate == 6 || this.baseRotate == 8){
30073             
30074                 height = this.thumbEl.getHeight();
30075                 this.baseScale = height / this.imageEl.OriginWidth;
30076
30077                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30078                     width = this.thumbEl.getWidth();
30079                     this.baseScale = width / this.imageEl.OriginHeight;
30080                 }
30081
30082                 return;
30083             }
30084
30085             height = this.thumbEl.getHeight();
30086             this.baseScale = height / this.imageEl.OriginHeight;
30087
30088             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30089                 width = this.thumbEl.getWidth();
30090                 this.baseScale = width / this.imageEl.OriginWidth;
30091             }
30092
30093             return;
30094         }
30095         
30096         if(this.baseRotate == 6 || this.baseRotate == 8){
30097             
30098             width = this.thumbEl.getHeight();
30099             this.baseScale = width / this.imageEl.OriginHeight;
30100             
30101             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30102                 height = this.thumbEl.getWidth();
30103                 this.baseScale = height / this.imageEl.OriginHeight;
30104             }
30105             
30106             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30107                 height = this.thumbEl.getWidth();
30108                 this.baseScale = height / this.imageEl.OriginHeight;
30109                 
30110                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30111                     width = this.thumbEl.getHeight();
30112                     this.baseScale = width / this.imageEl.OriginWidth;
30113                 }
30114             }
30115             
30116             return;
30117         }
30118         
30119         width = this.thumbEl.getWidth();
30120         this.baseScale = width / this.imageEl.OriginWidth;
30121         
30122         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30123             height = this.thumbEl.getHeight();
30124             this.baseScale = height / this.imageEl.OriginHeight;
30125         }
30126         
30127         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30128             
30129             height = this.thumbEl.getHeight();
30130             this.baseScale = height / this.imageEl.OriginHeight;
30131             
30132             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30133                 width = this.thumbEl.getWidth();
30134                 this.baseScale = width / this.imageEl.OriginWidth;
30135             }
30136             
30137         }
30138         
30139         return;
30140     },
30141     
30142     getScaleLevel : function()
30143     {
30144         return this.baseScale * Math.pow(1.1, this.scale);
30145     },
30146     
30147     onTouchStart : function(e)
30148     {
30149         if(!this.canvasLoaded){
30150             this.beforeSelectFile(e);
30151             return;
30152         }
30153         
30154         var touches = e.browserEvent.touches;
30155         
30156         if(!touches){
30157             return;
30158         }
30159         
30160         if(touches.length == 1){
30161             this.onMouseDown(e);
30162             return;
30163         }
30164         
30165         if(touches.length != 2){
30166             return;
30167         }
30168         
30169         var coords = [];
30170         
30171         for(var i = 0, finger; finger = touches[i]; i++){
30172             coords.push(finger.pageX, finger.pageY);
30173         }
30174         
30175         var x = Math.pow(coords[0] - coords[2], 2);
30176         var y = Math.pow(coords[1] - coords[3], 2);
30177         
30178         this.startDistance = Math.sqrt(x + y);
30179         
30180         this.startScale = this.scale;
30181         
30182         this.pinching = true;
30183         this.dragable = false;
30184         
30185     },
30186     
30187     onTouchMove : function(e)
30188     {
30189         if(!this.pinching && !this.dragable){
30190             return;
30191         }
30192         
30193         var touches = e.browserEvent.touches;
30194         
30195         if(!touches){
30196             return;
30197         }
30198         
30199         if(this.dragable){
30200             this.onMouseMove(e);
30201             return;
30202         }
30203         
30204         var coords = [];
30205         
30206         for(var i = 0, finger; finger = touches[i]; i++){
30207             coords.push(finger.pageX, finger.pageY);
30208         }
30209         
30210         var x = Math.pow(coords[0] - coords[2], 2);
30211         var y = Math.pow(coords[1] - coords[3], 2);
30212         
30213         this.endDistance = Math.sqrt(x + y);
30214         
30215         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30216         
30217         if(!this.zoomable()){
30218             this.scale = this.startScale;
30219             return;
30220         }
30221         
30222         this.draw();
30223         
30224     },
30225     
30226     onTouchEnd : function(e)
30227     {
30228         this.pinching = false;
30229         this.dragable = false;
30230         
30231     },
30232     
30233     process : function(file, crop)
30234     {
30235         if(this.loadMask){
30236             this.maskEl.mask(this.loadingText);
30237         }
30238         
30239         this.xhr = new XMLHttpRequest();
30240         
30241         file.xhr = this.xhr;
30242
30243         this.xhr.open(this.method, this.url, true);
30244         
30245         var headers = {
30246             "Accept": "application/json",
30247             "Cache-Control": "no-cache",
30248             "X-Requested-With": "XMLHttpRequest"
30249         };
30250         
30251         for (var headerName in headers) {
30252             var headerValue = headers[headerName];
30253             if (headerValue) {
30254                 this.xhr.setRequestHeader(headerName, headerValue);
30255             }
30256         }
30257         
30258         var _this = this;
30259         
30260         this.xhr.onload = function()
30261         {
30262             _this.xhrOnLoad(_this.xhr);
30263         }
30264         
30265         this.xhr.onerror = function()
30266         {
30267             _this.xhrOnError(_this.xhr);
30268         }
30269         
30270         var formData = new FormData();
30271
30272         formData.append('returnHTML', 'NO');
30273         
30274         if(crop){
30275             formData.append('crop', crop);
30276         }
30277         
30278         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30279             formData.append(this.paramName, file, file.name);
30280         }
30281         
30282         if(typeof(file.filename) != 'undefined'){
30283             formData.append('filename', file.filename);
30284         }
30285         
30286         if(typeof(file.mimetype) != 'undefined'){
30287             formData.append('mimetype', file.mimetype);
30288         }
30289         
30290         if(this.fireEvent('arrange', this, formData) != false){
30291             this.xhr.send(formData);
30292         };
30293     },
30294     
30295     xhrOnLoad : function(xhr)
30296     {
30297         if(this.loadMask){
30298             this.maskEl.unmask();
30299         }
30300         
30301         if (xhr.readyState !== 4) {
30302             this.fireEvent('exception', this, xhr);
30303             return;
30304         }
30305
30306         var response = Roo.decode(xhr.responseText);
30307         
30308         if(!response.success){
30309             this.fireEvent('exception', this, xhr);
30310             return;
30311         }
30312         
30313         var response = Roo.decode(xhr.responseText);
30314         
30315         this.fireEvent('upload', this, response);
30316         
30317     },
30318     
30319     xhrOnError : function()
30320     {
30321         if(this.loadMask){
30322             this.maskEl.unmask();
30323         }
30324         
30325         Roo.log('xhr on error');
30326         
30327         var response = Roo.decode(xhr.responseText);
30328           
30329         Roo.log(response);
30330         
30331     },
30332     
30333     prepare : function(file)
30334     {   
30335         if(this.loadMask){
30336             this.maskEl.mask(this.loadingText);
30337         }
30338         
30339         this.file = false;
30340         this.exif = {};
30341         
30342         if(typeof(file) === 'string'){
30343             this.loadCanvas(file);
30344             return;
30345         }
30346         
30347         if(!file || !this.urlAPI){
30348             return;
30349         }
30350         
30351         this.file = file;
30352         this.cropType = file.type;
30353         
30354         var _this = this;
30355         
30356         if(this.fireEvent('prepare', this, this.file) != false){
30357             
30358             var reader = new FileReader();
30359             
30360             reader.onload = function (e) {
30361                 if (e.target.error) {
30362                     Roo.log(e.target.error);
30363                     return;
30364                 }
30365                 
30366                 var buffer = e.target.result,
30367                     dataView = new DataView(buffer),
30368                     offset = 2,
30369                     maxOffset = dataView.byteLength - 4,
30370                     markerBytes,
30371                     markerLength;
30372                 
30373                 if (dataView.getUint16(0) === 0xffd8) {
30374                     while (offset < maxOffset) {
30375                         markerBytes = dataView.getUint16(offset);
30376                         
30377                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30378                             markerLength = dataView.getUint16(offset + 2) + 2;
30379                             if (offset + markerLength > dataView.byteLength) {
30380                                 Roo.log('Invalid meta data: Invalid segment size.');
30381                                 break;
30382                             }
30383                             
30384                             if(markerBytes == 0xffe1){
30385                                 _this.parseExifData(
30386                                     dataView,
30387                                     offset,
30388                                     markerLength
30389                                 );
30390                             }
30391                             
30392                             offset += markerLength;
30393                             
30394                             continue;
30395                         }
30396                         
30397                         break;
30398                     }
30399                     
30400                 }
30401                 
30402                 var url = _this.urlAPI.createObjectURL(_this.file);
30403                 
30404                 _this.loadCanvas(url);
30405                 
30406                 return;
30407             }
30408             
30409             reader.readAsArrayBuffer(this.file);
30410             
30411         }
30412         
30413     },
30414     
30415     parseExifData : function(dataView, offset, length)
30416     {
30417         var tiffOffset = offset + 10,
30418             littleEndian,
30419             dirOffset;
30420     
30421         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30422             // No Exif data, might be XMP data instead
30423             return;
30424         }
30425         
30426         // Check for the ASCII code for "Exif" (0x45786966):
30427         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30428             // No Exif data, might be XMP data instead
30429             return;
30430         }
30431         if (tiffOffset + 8 > dataView.byteLength) {
30432             Roo.log('Invalid Exif data: Invalid segment size.');
30433             return;
30434         }
30435         // Check for the two null bytes:
30436         if (dataView.getUint16(offset + 8) !== 0x0000) {
30437             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30438             return;
30439         }
30440         // Check the byte alignment:
30441         switch (dataView.getUint16(tiffOffset)) {
30442         case 0x4949:
30443             littleEndian = true;
30444             break;
30445         case 0x4D4D:
30446             littleEndian = false;
30447             break;
30448         default:
30449             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30450             return;
30451         }
30452         // Check for the TIFF tag marker (0x002A):
30453         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30454             Roo.log('Invalid Exif data: Missing TIFF marker.');
30455             return;
30456         }
30457         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30458         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30459         
30460         this.parseExifTags(
30461             dataView,
30462             tiffOffset,
30463             tiffOffset + dirOffset,
30464             littleEndian
30465         );
30466     },
30467     
30468     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30469     {
30470         var tagsNumber,
30471             dirEndOffset,
30472             i;
30473         if (dirOffset + 6 > dataView.byteLength) {
30474             Roo.log('Invalid Exif data: Invalid directory offset.');
30475             return;
30476         }
30477         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30478         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30479         if (dirEndOffset + 4 > dataView.byteLength) {
30480             Roo.log('Invalid Exif data: Invalid directory size.');
30481             return;
30482         }
30483         for (i = 0; i < tagsNumber; i += 1) {
30484             this.parseExifTag(
30485                 dataView,
30486                 tiffOffset,
30487                 dirOffset + 2 + 12 * i, // tag offset
30488                 littleEndian
30489             );
30490         }
30491         // Return the offset to the next directory:
30492         return dataView.getUint32(dirEndOffset, littleEndian);
30493     },
30494     
30495     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30496     {
30497         var tag = dataView.getUint16(offset, littleEndian);
30498         
30499         this.exif[tag] = this.getExifValue(
30500             dataView,
30501             tiffOffset,
30502             offset,
30503             dataView.getUint16(offset + 2, littleEndian), // tag type
30504             dataView.getUint32(offset + 4, littleEndian), // tag length
30505             littleEndian
30506         );
30507     },
30508     
30509     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30510     {
30511         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30512             tagSize,
30513             dataOffset,
30514             values,
30515             i,
30516             str,
30517             c;
30518     
30519         if (!tagType) {
30520             Roo.log('Invalid Exif data: Invalid tag type.');
30521             return;
30522         }
30523         
30524         tagSize = tagType.size * length;
30525         // Determine if the value is contained in the dataOffset bytes,
30526         // or if the value at the dataOffset is a pointer to the actual data:
30527         dataOffset = tagSize > 4 ?
30528                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30529         if (dataOffset + tagSize > dataView.byteLength) {
30530             Roo.log('Invalid Exif data: Invalid data offset.');
30531             return;
30532         }
30533         if (length === 1) {
30534             return tagType.getValue(dataView, dataOffset, littleEndian);
30535         }
30536         values = [];
30537         for (i = 0; i < length; i += 1) {
30538             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30539         }
30540         
30541         if (tagType.ascii) {
30542             str = '';
30543             // Concatenate the chars:
30544             for (i = 0; i < values.length; i += 1) {
30545                 c = values[i];
30546                 // Ignore the terminating NULL byte(s):
30547                 if (c === '\u0000') {
30548                     break;
30549                 }
30550                 str += c;
30551             }
30552             return str;
30553         }
30554         return values;
30555     }
30556     
30557 });
30558
30559 Roo.apply(Roo.bootstrap.UploadCropbox, {
30560     tags : {
30561         'Orientation': 0x0112
30562     },
30563     
30564     Orientation: {
30565             1: 0, //'top-left',
30566 //            2: 'top-right',
30567             3: 180, //'bottom-right',
30568 //            4: 'bottom-left',
30569 //            5: 'left-top',
30570             6: 90, //'right-top',
30571 //            7: 'right-bottom',
30572             8: 270 //'left-bottom'
30573     },
30574     
30575     exifTagTypes : {
30576         // byte, 8-bit unsigned int:
30577         1: {
30578             getValue: function (dataView, dataOffset) {
30579                 return dataView.getUint8(dataOffset);
30580             },
30581             size: 1
30582         },
30583         // ascii, 8-bit byte:
30584         2: {
30585             getValue: function (dataView, dataOffset) {
30586                 return String.fromCharCode(dataView.getUint8(dataOffset));
30587             },
30588             size: 1,
30589             ascii: true
30590         },
30591         // short, 16 bit int:
30592         3: {
30593             getValue: function (dataView, dataOffset, littleEndian) {
30594                 return dataView.getUint16(dataOffset, littleEndian);
30595             },
30596             size: 2
30597         },
30598         // long, 32 bit int:
30599         4: {
30600             getValue: function (dataView, dataOffset, littleEndian) {
30601                 return dataView.getUint32(dataOffset, littleEndian);
30602             },
30603             size: 4
30604         },
30605         // rational = two long values, first is numerator, second is denominator:
30606         5: {
30607             getValue: function (dataView, dataOffset, littleEndian) {
30608                 return dataView.getUint32(dataOffset, littleEndian) /
30609                     dataView.getUint32(dataOffset + 4, littleEndian);
30610             },
30611             size: 8
30612         },
30613         // slong, 32 bit signed int:
30614         9: {
30615             getValue: function (dataView, dataOffset, littleEndian) {
30616                 return dataView.getInt32(dataOffset, littleEndian);
30617             },
30618             size: 4
30619         },
30620         // srational, two slongs, first is numerator, second is denominator:
30621         10: {
30622             getValue: function (dataView, dataOffset, littleEndian) {
30623                 return dataView.getInt32(dataOffset, littleEndian) /
30624                     dataView.getInt32(dataOffset + 4, littleEndian);
30625             },
30626             size: 8
30627         }
30628     },
30629     
30630     footer : {
30631         STANDARD : [
30632             {
30633                 tag : 'div',
30634                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30635                 action : 'rotate-left',
30636                 cn : [
30637                     {
30638                         tag : 'button',
30639                         cls : 'btn btn-default',
30640                         html : '<i class="fa fa-undo"></i>'
30641                     }
30642                 ]
30643             },
30644             {
30645                 tag : 'div',
30646                 cls : 'btn-group roo-upload-cropbox-picture',
30647                 action : 'picture',
30648                 cn : [
30649                     {
30650                         tag : 'button',
30651                         cls : 'btn btn-default',
30652                         html : '<i class="fa fa-picture-o"></i>'
30653                     }
30654                 ]
30655             },
30656             {
30657                 tag : 'div',
30658                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30659                 action : 'rotate-right',
30660                 cn : [
30661                     {
30662                         tag : 'button',
30663                         cls : 'btn btn-default',
30664                         html : '<i class="fa fa-repeat"></i>'
30665                     }
30666                 ]
30667             }
30668         ],
30669         DOCUMENT : [
30670             {
30671                 tag : 'div',
30672                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30673                 action : 'rotate-left',
30674                 cn : [
30675                     {
30676                         tag : 'button',
30677                         cls : 'btn btn-default',
30678                         html : '<i class="fa fa-undo"></i>'
30679                     }
30680                 ]
30681             },
30682             {
30683                 tag : 'div',
30684                 cls : 'btn-group roo-upload-cropbox-download',
30685                 action : 'download',
30686                 cn : [
30687                     {
30688                         tag : 'button',
30689                         cls : 'btn btn-default',
30690                         html : '<i class="fa fa-download"></i>'
30691                     }
30692                 ]
30693             },
30694             {
30695                 tag : 'div',
30696                 cls : 'btn-group roo-upload-cropbox-crop',
30697                 action : 'crop',
30698                 cn : [
30699                     {
30700                         tag : 'button',
30701                         cls : 'btn btn-default',
30702                         html : '<i class="fa fa-crop"></i>'
30703                     }
30704                 ]
30705             },
30706             {
30707                 tag : 'div',
30708                 cls : 'btn-group roo-upload-cropbox-trash',
30709                 action : 'trash',
30710                 cn : [
30711                     {
30712                         tag : 'button',
30713                         cls : 'btn btn-default',
30714                         html : '<i class="fa fa-trash"></i>'
30715                     }
30716                 ]
30717             },
30718             {
30719                 tag : 'div',
30720                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30721                 action : 'rotate-right',
30722                 cn : [
30723                     {
30724                         tag : 'button',
30725                         cls : 'btn btn-default',
30726                         html : '<i class="fa fa-repeat"></i>'
30727                     }
30728                 ]
30729             }
30730         ],
30731         ROTATOR : [
30732             {
30733                 tag : 'div',
30734                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30735                 action : 'rotate-left',
30736                 cn : [
30737                     {
30738                         tag : 'button',
30739                         cls : 'btn btn-default',
30740                         html : '<i class="fa fa-undo"></i>'
30741                     }
30742                 ]
30743             },
30744             {
30745                 tag : 'div',
30746                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30747                 action : 'rotate-right',
30748                 cn : [
30749                     {
30750                         tag : 'button',
30751                         cls : 'btn btn-default',
30752                         html : '<i class="fa fa-repeat"></i>'
30753                     }
30754                 ]
30755             }
30756         ]
30757     }
30758 });
30759
30760 /*
30761 * Licence: LGPL
30762 */
30763
30764 /**
30765  * @class Roo.bootstrap.DocumentManager
30766  * @extends Roo.bootstrap.Component
30767  * Bootstrap DocumentManager class
30768  * @cfg {String} paramName default 'imageUpload'
30769  * @cfg {String} toolTipName default 'filename'
30770  * @cfg {String} method default POST
30771  * @cfg {String} url action url
30772  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30773  * @cfg {Boolean} multiple multiple upload default true
30774  * @cfg {Number} thumbSize default 300
30775  * @cfg {String} fieldLabel
30776  * @cfg {Number} labelWidth default 4
30777  * @cfg {String} labelAlign (left|top) default left
30778  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30779 * @cfg {Number} labellg set the width of label (1-12)
30780  * @cfg {Number} labelmd set the width of label (1-12)
30781  * @cfg {Number} labelsm set the width of label (1-12)
30782  * @cfg {Number} labelxs set the width of label (1-12)
30783  * 
30784  * @constructor
30785  * Create a new DocumentManager
30786  * @param {Object} config The config object
30787  */
30788
30789 Roo.bootstrap.DocumentManager = function(config){
30790     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30791     
30792     this.files = [];
30793     this.delegates = [];
30794     
30795     this.addEvents({
30796         /**
30797          * @event initial
30798          * Fire when initial the DocumentManager
30799          * @param {Roo.bootstrap.DocumentManager} this
30800          */
30801         "initial" : true,
30802         /**
30803          * @event inspect
30804          * inspect selected file
30805          * @param {Roo.bootstrap.DocumentManager} this
30806          * @param {File} file
30807          */
30808         "inspect" : true,
30809         /**
30810          * @event exception
30811          * Fire when xhr load exception
30812          * @param {Roo.bootstrap.DocumentManager} this
30813          * @param {XMLHttpRequest} xhr
30814          */
30815         "exception" : true,
30816         /**
30817          * @event afterupload
30818          * Fire when xhr load exception
30819          * @param {Roo.bootstrap.DocumentManager} this
30820          * @param {XMLHttpRequest} xhr
30821          */
30822         "afterupload" : true,
30823         /**
30824          * @event prepare
30825          * prepare the form data
30826          * @param {Roo.bootstrap.DocumentManager} this
30827          * @param {Object} formData
30828          */
30829         "prepare" : true,
30830         /**
30831          * @event remove
30832          * Fire when remove the file
30833          * @param {Roo.bootstrap.DocumentManager} this
30834          * @param {Object} file
30835          */
30836         "remove" : true,
30837         /**
30838          * @event refresh
30839          * Fire after refresh the file
30840          * @param {Roo.bootstrap.DocumentManager} this
30841          */
30842         "refresh" : true,
30843         /**
30844          * @event click
30845          * Fire after click the image
30846          * @param {Roo.bootstrap.DocumentManager} this
30847          * @param {Object} file
30848          */
30849         "click" : true,
30850         /**
30851          * @event edit
30852          * Fire when upload a image and editable set to true
30853          * @param {Roo.bootstrap.DocumentManager} this
30854          * @param {Object} file
30855          */
30856         "edit" : true,
30857         /**
30858          * @event beforeselectfile
30859          * Fire before select file
30860          * @param {Roo.bootstrap.DocumentManager} this
30861          */
30862         "beforeselectfile" : true,
30863         /**
30864          * @event process
30865          * Fire before process file
30866          * @param {Roo.bootstrap.DocumentManager} this
30867          * @param {Object} file
30868          */
30869         "process" : true,
30870         /**
30871          * @event previewrendered
30872          * Fire when preview rendered
30873          * @param {Roo.bootstrap.DocumentManager} this
30874          * @param {Object} file
30875          */
30876         "previewrendered" : true,
30877         /**
30878          */
30879         "previewResize" : true
30880         
30881     });
30882 };
30883
30884 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30885     
30886     boxes : 0,
30887     inputName : '',
30888     thumbSize : 300,
30889     multiple : true,
30890     files : false,
30891     method : 'POST',
30892     url : '',
30893     paramName : 'imageUpload',
30894     toolTipName : 'filename',
30895     fieldLabel : '',
30896     labelWidth : 4,
30897     labelAlign : 'left',
30898     editable : true,
30899     delegates : false,
30900     xhr : false, 
30901     
30902     labellg : 0,
30903     labelmd : 0,
30904     labelsm : 0,
30905     labelxs : 0,
30906     
30907     getAutoCreate : function()
30908     {   
30909         var managerWidget = {
30910             tag : 'div',
30911             cls : 'roo-document-manager',
30912             cn : [
30913                 {
30914                     tag : 'input',
30915                     cls : 'roo-document-manager-selector',
30916                     type : 'file'
30917                 },
30918                 {
30919                     tag : 'div',
30920                     cls : 'roo-document-manager-uploader',
30921                     cn : [
30922                         {
30923                             tag : 'div',
30924                             cls : 'roo-document-manager-upload-btn',
30925                             html : '<i class="fa fa-plus"></i>'
30926                         }
30927                     ]
30928                     
30929                 }
30930             ]
30931         };
30932         
30933         var content = [
30934             {
30935                 tag : 'div',
30936                 cls : 'column col-md-12',
30937                 cn : managerWidget
30938             }
30939         ];
30940         
30941         if(this.fieldLabel.length){
30942             
30943             content = [
30944                 {
30945                     tag : 'div',
30946                     cls : 'column col-md-12',
30947                     html : this.fieldLabel
30948                 },
30949                 {
30950                     tag : 'div',
30951                     cls : 'column col-md-12',
30952                     cn : managerWidget
30953                 }
30954             ];
30955
30956             if(this.labelAlign == 'left'){
30957                 content = [
30958                     {
30959                         tag : 'div',
30960                         cls : 'column',
30961                         html : this.fieldLabel
30962                     },
30963                     {
30964                         tag : 'div',
30965                         cls : 'column',
30966                         cn : managerWidget
30967                     }
30968                 ];
30969                 
30970                 if(this.labelWidth > 12){
30971                     content[0].style = "width: " + this.labelWidth + 'px';
30972                 }
30973
30974                 if(this.labelWidth < 13 && this.labelmd == 0){
30975                     this.labelmd = this.labelWidth;
30976                 }
30977
30978                 if(this.labellg > 0){
30979                     content[0].cls += ' col-lg-' + this.labellg;
30980                     content[1].cls += ' col-lg-' + (12 - this.labellg);
30981                 }
30982
30983                 if(this.labelmd > 0){
30984                     content[0].cls += ' col-md-' + this.labelmd;
30985                     content[1].cls += ' col-md-' + (12 - this.labelmd);
30986                 }
30987
30988                 if(this.labelsm > 0){
30989                     content[0].cls += ' col-sm-' + this.labelsm;
30990                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
30991                 }
30992
30993                 if(this.labelxs > 0){
30994                     content[0].cls += ' col-xs-' + this.labelxs;
30995                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
30996                 }
30997                 
30998             }
30999         }
31000         
31001         var cfg = {
31002             tag : 'div',
31003             cls : 'row clearfix',
31004             cn : content
31005         };
31006         
31007         return cfg;
31008         
31009     },
31010     
31011     initEvents : function()
31012     {
31013         this.managerEl = this.el.select('.roo-document-manager', true).first();
31014         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31015         
31016         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31017         this.selectorEl.hide();
31018         
31019         if(this.multiple){
31020             this.selectorEl.attr('multiple', 'multiple');
31021         }
31022         
31023         this.selectorEl.on('change', this.onFileSelected, this);
31024         
31025         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31026         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31027         
31028         this.uploader.on('click', this.onUploaderClick, this);
31029         
31030         this.renderProgressDialog();
31031         
31032         var _this = this;
31033         
31034         window.addEventListener("resize", function() { _this.refresh(); } );
31035         
31036         this.fireEvent('initial', this);
31037     },
31038     
31039     renderProgressDialog : function()
31040     {
31041         var _this = this;
31042         
31043         this.progressDialog = new Roo.bootstrap.Modal({
31044             cls : 'roo-document-manager-progress-dialog',
31045             allow_close : false,
31046             animate : false,
31047             title : '',
31048             buttons : [
31049                 {
31050                     name  :'cancel',
31051                     weight : 'danger',
31052                     html : 'Cancel'
31053                 }
31054             ], 
31055             listeners : { 
31056                 btnclick : function() {
31057                     _this.uploadCancel();
31058                     this.hide();
31059                 }
31060             }
31061         });
31062          
31063         this.progressDialog.render(Roo.get(document.body));
31064          
31065         this.progress = new Roo.bootstrap.Progress({
31066             cls : 'roo-document-manager-progress',
31067             active : true,
31068             striped : true
31069         });
31070         
31071         this.progress.render(this.progressDialog.getChildContainer());
31072         
31073         this.progressBar = new Roo.bootstrap.ProgressBar({
31074             cls : 'roo-document-manager-progress-bar',
31075             aria_valuenow : 0,
31076             aria_valuemin : 0,
31077             aria_valuemax : 12,
31078             panel : 'success'
31079         });
31080         
31081         this.progressBar.render(this.progress.getChildContainer());
31082     },
31083     
31084     onUploaderClick : function(e)
31085     {
31086         e.preventDefault();
31087      
31088         if(this.fireEvent('beforeselectfile', this) != false){
31089             this.selectorEl.dom.click();
31090         }
31091         
31092     },
31093     
31094     onFileSelected : function(e)
31095     {
31096         e.preventDefault();
31097         
31098         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31099             return;
31100         }
31101         
31102         Roo.each(this.selectorEl.dom.files, function(file){
31103             if(this.fireEvent('inspect', this, file) != false){
31104                 this.files.push(file);
31105             }
31106         }, this);
31107         
31108         this.queue();
31109         
31110     },
31111     
31112     queue : function()
31113     {
31114         this.selectorEl.dom.value = '';
31115         
31116         if(!this.files || !this.files.length){
31117             return;
31118         }
31119         
31120         if(this.boxes > 0 && this.files.length > this.boxes){
31121             this.files = this.files.slice(0, this.boxes);
31122         }
31123         
31124         this.uploader.show();
31125         
31126         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31127             this.uploader.hide();
31128         }
31129         
31130         var _this = this;
31131         
31132         var files = [];
31133         
31134         var docs = [];
31135         
31136         Roo.each(this.files, function(file){
31137             
31138             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31139                 var f = this.renderPreview(file);
31140                 files.push(f);
31141                 return;
31142             }
31143             
31144             if(file.type.indexOf('image') != -1){
31145                 this.delegates.push(
31146                     (function(){
31147                         _this.process(file);
31148                     }).createDelegate(this)
31149                 );
31150         
31151                 return;
31152             }
31153             
31154             docs.push(
31155                 (function(){
31156                     _this.process(file);
31157                 }).createDelegate(this)
31158             );
31159             
31160         }, this);
31161         
31162         this.files = files;
31163         
31164         this.delegates = this.delegates.concat(docs);
31165         
31166         if(!this.delegates.length){
31167             this.refresh();
31168             return;
31169         }
31170         
31171         this.progressBar.aria_valuemax = this.delegates.length;
31172         
31173         this.arrange();
31174         
31175         return;
31176     },
31177     
31178     arrange : function()
31179     {
31180         if(!this.delegates.length){
31181             this.progressDialog.hide();
31182             this.refresh();
31183             return;
31184         }
31185         
31186         var delegate = this.delegates.shift();
31187         
31188         this.progressDialog.show();
31189         
31190         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31191         
31192         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31193         
31194         delegate();
31195     },
31196     
31197     refresh : function()
31198     {
31199         this.uploader.show();
31200         
31201         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31202             this.uploader.hide();
31203         }
31204         
31205         Roo.isTouch ? this.closable(false) : this.closable(true);
31206         
31207         this.fireEvent('refresh', this);
31208     },
31209     
31210     onRemove : function(e, el, o)
31211     {
31212         e.preventDefault();
31213         
31214         this.fireEvent('remove', this, o);
31215         
31216     },
31217     
31218     remove : function(o)
31219     {
31220         var files = [];
31221         
31222         Roo.each(this.files, function(file){
31223             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31224                 files.push(file);
31225                 return;
31226             }
31227
31228             o.target.remove();
31229
31230         }, this);
31231         
31232         this.files = files;
31233         
31234         this.refresh();
31235     },
31236     
31237     clear : function()
31238     {
31239         Roo.each(this.files, function(file){
31240             if(!file.target){
31241                 return;
31242             }
31243             
31244             file.target.remove();
31245
31246         }, this);
31247         
31248         this.files = [];
31249         
31250         this.refresh();
31251     },
31252     
31253     onClick : function(e, el, o)
31254     {
31255         e.preventDefault();
31256         
31257         this.fireEvent('click', this, o);
31258         
31259     },
31260     
31261     closable : function(closable)
31262     {
31263         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31264             
31265             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31266             
31267             if(closable){
31268                 el.show();
31269                 return;
31270             }
31271             
31272             el.hide();
31273             
31274         }, this);
31275     },
31276     
31277     xhrOnLoad : function(xhr)
31278     {
31279         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31280             el.remove();
31281         }, this);
31282         
31283         if (xhr.readyState !== 4) {
31284             this.arrange();
31285             this.fireEvent('exception', this, xhr);
31286             return;
31287         }
31288
31289         var response = Roo.decode(xhr.responseText);
31290         
31291         if(!response.success){
31292             this.arrange();
31293             this.fireEvent('exception', this, xhr);
31294             return;
31295         }
31296         
31297         var file = this.renderPreview(response.data);
31298         
31299         this.files.push(file);
31300         
31301         this.arrange();
31302         
31303         this.fireEvent('afterupload', this, xhr);
31304         
31305     },
31306     
31307     xhrOnError : function(xhr)
31308     {
31309         Roo.log('xhr on error');
31310         
31311         var response = Roo.decode(xhr.responseText);
31312           
31313         Roo.log(response);
31314         
31315         this.arrange();
31316     },
31317     
31318     process : function(file)
31319     {
31320         if(this.fireEvent('process', this, file) !== false){
31321             if(this.editable && file.type.indexOf('image') != -1){
31322                 this.fireEvent('edit', this, file);
31323                 return;
31324             }
31325
31326             this.uploadStart(file, false);
31327
31328             return;
31329         }
31330         
31331     },
31332     
31333     uploadStart : function(file, crop)
31334     {
31335         this.xhr = new XMLHttpRequest();
31336         
31337         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31338             this.arrange();
31339             return;
31340         }
31341         
31342         file.xhr = this.xhr;
31343             
31344         this.managerEl.createChild({
31345             tag : 'div',
31346             cls : 'roo-document-manager-loading',
31347             cn : [
31348                 {
31349                     tag : 'div',
31350                     tooltip : file.name,
31351                     cls : 'roo-document-manager-thumb',
31352                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31353                 }
31354             ]
31355
31356         });
31357
31358         this.xhr.open(this.method, this.url, true);
31359         
31360         var headers = {
31361             "Accept": "application/json",
31362             "Cache-Control": "no-cache",
31363             "X-Requested-With": "XMLHttpRequest"
31364         };
31365         
31366         for (var headerName in headers) {
31367             var headerValue = headers[headerName];
31368             if (headerValue) {
31369                 this.xhr.setRequestHeader(headerName, headerValue);
31370             }
31371         }
31372         
31373         var _this = this;
31374         
31375         this.xhr.onload = function()
31376         {
31377             _this.xhrOnLoad(_this.xhr);
31378         }
31379         
31380         this.xhr.onerror = function()
31381         {
31382             _this.xhrOnError(_this.xhr);
31383         }
31384         
31385         var formData = new FormData();
31386
31387         formData.append('returnHTML', 'NO');
31388         
31389         if(crop){
31390             formData.append('crop', crop);
31391         }
31392         
31393         formData.append(this.paramName, file, file.name);
31394         
31395         var options = {
31396             file : file, 
31397             manually : false
31398         };
31399         
31400         if(this.fireEvent('prepare', this, formData, options) != false){
31401             
31402             if(options.manually){
31403                 return;
31404             }
31405             
31406             this.xhr.send(formData);
31407             return;
31408         };
31409         
31410         this.uploadCancel();
31411     },
31412     
31413     uploadCancel : function()
31414     {
31415         if (this.xhr) {
31416             this.xhr.abort();
31417         }
31418         
31419         this.delegates = [];
31420         
31421         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31422             el.remove();
31423         }, this);
31424         
31425         this.arrange();
31426     },
31427     
31428     renderPreview : function(file)
31429     {
31430         if(typeof(file.target) != 'undefined' && file.target){
31431             return file;
31432         }
31433         
31434         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31435         
31436         var previewEl = this.managerEl.createChild({
31437             tag : 'div',
31438             cls : 'roo-document-manager-preview',
31439             cn : [
31440                 {
31441                     tag : 'div',
31442                     tooltip : file[this.toolTipName],
31443                     cls : 'roo-document-manager-thumb',
31444                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31445                 },
31446                 {
31447                     tag : 'button',
31448                     cls : 'close',
31449                     html : '<i class="fa fa-times-circle"></i>'
31450                 }
31451             ]
31452         });
31453
31454         var close = previewEl.select('button.close', true).first();
31455
31456         close.on('click', this.onRemove, this, file);
31457
31458         file.target = previewEl;
31459
31460         var image = previewEl.select('img', true).first();
31461         
31462         var _this = this;
31463         
31464         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31465         
31466         image.on('click', this.onClick, this, file);
31467         
31468         this.fireEvent('previewrendered', this, file);
31469         
31470         return file;
31471         
31472     },
31473     
31474     onPreviewLoad : function(file, image)
31475     {
31476         if(typeof(file.target) == 'undefined' || !file.target){
31477             return;
31478         }
31479         
31480         var width = image.dom.naturalWidth || image.dom.width;
31481         var height = image.dom.naturalHeight || image.dom.height;
31482         
31483         if(!this.previewResize) {
31484             return;
31485         }
31486         
31487         if(width > height){
31488             file.target.addClass('wide');
31489             return;
31490         }
31491         
31492         file.target.addClass('tall');
31493         return;
31494         
31495     },
31496     
31497     uploadFromSource : function(file, crop)
31498     {
31499         this.xhr = new XMLHttpRequest();
31500         
31501         this.managerEl.createChild({
31502             tag : 'div',
31503             cls : 'roo-document-manager-loading',
31504             cn : [
31505                 {
31506                     tag : 'div',
31507                     tooltip : file.name,
31508                     cls : 'roo-document-manager-thumb',
31509                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31510                 }
31511             ]
31512
31513         });
31514
31515         this.xhr.open(this.method, this.url, true);
31516         
31517         var headers = {
31518             "Accept": "application/json",
31519             "Cache-Control": "no-cache",
31520             "X-Requested-With": "XMLHttpRequest"
31521         };
31522         
31523         for (var headerName in headers) {
31524             var headerValue = headers[headerName];
31525             if (headerValue) {
31526                 this.xhr.setRequestHeader(headerName, headerValue);
31527             }
31528         }
31529         
31530         var _this = this;
31531         
31532         this.xhr.onload = function()
31533         {
31534             _this.xhrOnLoad(_this.xhr);
31535         }
31536         
31537         this.xhr.onerror = function()
31538         {
31539             _this.xhrOnError(_this.xhr);
31540         }
31541         
31542         var formData = new FormData();
31543
31544         formData.append('returnHTML', 'NO');
31545         
31546         formData.append('crop', crop);
31547         
31548         if(typeof(file.filename) != 'undefined'){
31549             formData.append('filename', file.filename);
31550         }
31551         
31552         if(typeof(file.mimetype) != 'undefined'){
31553             formData.append('mimetype', file.mimetype);
31554         }
31555         
31556         Roo.log(formData);
31557         
31558         if(this.fireEvent('prepare', this, formData) != false){
31559             this.xhr.send(formData);
31560         };
31561     }
31562 });
31563
31564 /*
31565 * Licence: LGPL
31566 */
31567
31568 /**
31569  * @class Roo.bootstrap.DocumentViewer
31570  * @extends Roo.bootstrap.Component
31571  * Bootstrap DocumentViewer class
31572  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31573  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31574  * 
31575  * @constructor
31576  * Create a new DocumentViewer
31577  * @param {Object} config The config object
31578  */
31579
31580 Roo.bootstrap.DocumentViewer = function(config){
31581     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31582     
31583     this.addEvents({
31584         /**
31585          * @event initial
31586          * Fire after initEvent
31587          * @param {Roo.bootstrap.DocumentViewer} this
31588          */
31589         "initial" : true,
31590         /**
31591          * @event click
31592          * Fire after click
31593          * @param {Roo.bootstrap.DocumentViewer} this
31594          */
31595         "click" : true,
31596         /**
31597          * @event download
31598          * Fire after download button
31599          * @param {Roo.bootstrap.DocumentViewer} this
31600          */
31601         "download" : true,
31602         /**
31603          * @event trash
31604          * Fire after trash button
31605          * @param {Roo.bootstrap.DocumentViewer} this
31606          */
31607         "trash" : true
31608         
31609     });
31610 };
31611
31612 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31613     
31614     showDownload : true,
31615     
31616     showTrash : true,
31617     
31618     getAutoCreate : function()
31619     {
31620         var cfg = {
31621             tag : 'div',
31622             cls : 'roo-document-viewer',
31623             cn : [
31624                 {
31625                     tag : 'div',
31626                     cls : 'roo-document-viewer-body',
31627                     cn : [
31628                         {
31629                             tag : 'div',
31630                             cls : 'roo-document-viewer-thumb',
31631                             cn : [
31632                                 {
31633                                     tag : 'img',
31634                                     cls : 'roo-document-viewer-image'
31635                                 }
31636                             ]
31637                         }
31638                     ]
31639                 },
31640                 {
31641                     tag : 'div',
31642                     cls : 'roo-document-viewer-footer',
31643                     cn : {
31644                         tag : 'div',
31645                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31646                         cn : [
31647                             {
31648                                 tag : 'div',
31649                                 cls : 'btn-group roo-document-viewer-download',
31650                                 cn : [
31651                                     {
31652                                         tag : 'button',
31653                                         cls : 'btn btn-default',
31654                                         html : '<i class="fa fa-download"></i>'
31655                                     }
31656                                 ]
31657                             },
31658                             {
31659                                 tag : 'div',
31660                                 cls : 'btn-group roo-document-viewer-trash',
31661                                 cn : [
31662                                     {
31663                                         tag : 'button',
31664                                         cls : 'btn btn-default',
31665                                         html : '<i class="fa fa-trash"></i>'
31666                                     }
31667                                 ]
31668                             }
31669                         ]
31670                     }
31671                 }
31672             ]
31673         };
31674         
31675         return cfg;
31676     },
31677     
31678     initEvents : function()
31679     {
31680         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31681         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31682         
31683         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31684         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31685         
31686         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31687         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31688         
31689         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31690         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31691         
31692         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31693         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31694         
31695         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31696         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31697         
31698         this.bodyEl.on('click', this.onClick, this);
31699         this.downloadBtn.on('click', this.onDownload, this);
31700         this.trashBtn.on('click', this.onTrash, this);
31701         
31702         this.downloadBtn.hide();
31703         this.trashBtn.hide();
31704         
31705         if(this.showDownload){
31706             this.downloadBtn.show();
31707         }
31708         
31709         if(this.showTrash){
31710             this.trashBtn.show();
31711         }
31712         
31713         if(!this.showDownload && !this.showTrash) {
31714             this.footerEl.hide();
31715         }
31716         
31717     },
31718     
31719     initial : function()
31720     {
31721         this.fireEvent('initial', this);
31722         
31723     },
31724     
31725     onClick : function(e)
31726     {
31727         e.preventDefault();
31728         
31729         this.fireEvent('click', this);
31730     },
31731     
31732     onDownload : function(e)
31733     {
31734         e.preventDefault();
31735         
31736         this.fireEvent('download', this);
31737     },
31738     
31739     onTrash : function(e)
31740     {
31741         e.preventDefault();
31742         
31743         this.fireEvent('trash', this);
31744     }
31745     
31746 });
31747 /*
31748  * - LGPL
31749  *
31750  * nav progress bar
31751  * 
31752  */
31753
31754 /**
31755  * @class Roo.bootstrap.NavProgressBar
31756  * @extends Roo.bootstrap.Component
31757  * Bootstrap NavProgressBar class
31758  * 
31759  * @constructor
31760  * Create a new nav progress bar
31761  * @param {Object} config The config object
31762  */
31763
31764 Roo.bootstrap.NavProgressBar = function(config){
31765     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31766
31767     this.bullets = this.bullets || [];
31768    
31769 //    Roo.bootstrap.NavProgressBar.register(this);
31770      this.addEvents({
31771         /**
31772              * @event changed
31773              * Fires when the active item changes
31774              * @param {Roo.bootstrap.NavProgressBar} this
31775              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31776              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31777          */
31778         'changed': true
31779      });
31780     
31781 };
31782
31783 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31784     
31785     bullets : [],
31786     barItems : [],
31787     
31788     getAutoCreate : function()
31789     {
31790         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31791         
31792         cfg = {
31793             tag : 'div',
31794             cls : 'roo-navigation-bar-group',
31795             cn : [
31796                 {
31797                     tag : 'div',
31798                     cls : 'roo-navigation-top-bar'
31799                 },
31800                 {
31801                     tag : 'div',
31802                     cls : 'roo-navigation-bullets-bar',
31803                     cn : [
31804                         {
31805                             tag : 'ul',
31806                             cls : 'roo-navigation-bar'
31807                         }
31808                     ]
31809                 },
31810                 
31811                 {
31812                     tag : 'div',
31813                     cls : 'roo-navigation-bottom-bar'
31814                 }
31815             ]
31816             
31817         };
31818         
31819         return cfg;
31820         
31821     },
31822     
31823     initEvents: function() 
31824     {
31825         
31826     },
31827     
31828     onRender : function(ct, position) 
31829     {
31830         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31831         
31832         if(this.bullets.length){
31833             Roo.each(this.bullets, function(b){
31834                this.addItem(b);
31835             }, this);
31836         }
31837         
31838         this.format();
31839         
31840     },
31841     
31842     addItem : function(cfg)
31843     {
31844         var item = new Roo.bootstrap.NavProgressItem(cfg);
31845         
31846         item.parentId = this.id;
31847         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31848         
31849         if(cfg.html){
31850             var top = new Roo.bootstrap.Element({
31851                 tag : 'div',
31852                 cls : 'roo-navigation-bar-text'
31853             });
31854             
31855             var bottom = new Roo.bootstrap.Element({
31856                 tag : 'div',
31857                 cls : 'roo-navigation-bar-text'
31858             });
31859             
31860             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31861             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31862             
31863             var topText = new Roo.bootstrap.Element({
31864                 tag : 'span',
31865                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31866             });
31867             
31868             var bottomText = new Roo.bootstrap.Element({
31869                 tag : 'span',
31870                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31871             });
31872             
31873             topText.onRender(top.el, null);
31874             bottomText.onRender(bottom.el, null);
31875             
31876             item.topEl = top;
31877             item.bottomEl = bottom;
31878         }
31879         
31880         this.barItems.push(item);
31881         
31882         return item;
31883     },
31884     
31885     getActive : function()
31886     {
31887         var active = false;
31888         
31889         Roo.each(this.barItems, function(v){
31890             
31891             if (!v.isActive()) {
31892                 return;
31893             }
31894             
31895             active = v;
31896             return false;
31897             
31898         });
31899         
31900         return active;
31901     },
31902     
31903     setActiveItem : function(item)
31904     {
31905         var prev = false;
31906         
31907         Roo.each(this.barItems, function(v){
31908             if (v.rid == item.rid) {
31909                 return ;
31910             }
31911             
31912             if (v.isActive()) {
31913                 v.setActive(false);
31914                 prev = v;
31915             }
31916         });
31917
31918         item.setActive(true);
31919         
31920         this.fireEvent('changed', this, item, prev);
31921     },
31922     
31923     getBarItem: function(rid)
31924     {
31925         var ret = false;
31926         
31927         Roo.each(this.barItems, function(e) {
31928             if (e.rid != rid) {
31929                 return;
31930             }
31931             
31932             ret =  e;
31933             return false;
31934         });
31935         
31936         return ret;
31937     },
31938     
31939     indexOfItem : function(item)
31940     {
31941         var index = false;
31942         
31943         Roo.each(this.barItems, function(v, i){
31944             
31945             if (v.rid != item.rid) {
31946                 return;
31947             }
31948             
31949             index = i;
31950             return false
31951         });
31952         
31953         return index;
31954     },
31955     
31956     setActiveNext : function()
31957     {
31958         var i = this.indexOfItem(this.getActive());
31959         
31960         if (i > this.barItems.length) {
31961             return;
31962         }
31963         
31964         this.setActiveItem(this.barItems[i+1]);
31965     },
31966     
31967     setActivePrev : function()
31968     {
31969         var i = this.indexOfItem(this.getActive());
31970         
31971         if (i  < 1) {
31972             return;
31973         }
31974         
31975         this.setActiveItem(this.barItems[i-1]);
31976     },
31977     
31978     format : function()
31979     {
31980         if(!this.barItems.length){
31981             return;
31982         }
31983      
31984         var width = 100 / this.barItems.length;
31985         
31986         Roo.each(this.barItems, function(i){
31987             i.el.setStyle('width', width + '%');
31988             i.topEl.el.setStyle('width', width + '%');
31989             i.bottomEl.el.setStyle('width', width + '%');
31990         }, this);
31991         
31992     }
31993     
31994 });
31995 /*
31996  * - LGPL
31997  *
31998  * Nav Progress Item
31999  * 
32000  */
32001
32002 /**
32003  * @class Roo.bootstrap.NavProgressItem
32004  * @extends Roo.bootstrap.Component
32005  * Bootstrap NavProgressItem class
32006  * @cfg {String} rid the reference id
32007  * @cfg {Boolean} active (true|false) Is item active default false
32008  * @cfg {Boolean} disabled (true|false) Is item active default false
32009  * @cfg {String} html
32010  * @cfg {String} position (top|bottom) text position default bottom
32011  * @cfg {String} icon show icon instead of number
32012  * 
32013  * @constructor
32014  * Create a new NavProgressItem
32015  * @param {Object} config The config object
32016  */
32017 Roo.bootstrap.NavProgressItem = function(config){
32018     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32019     this.addEvents({
32020         // raw events
32021         /**
32022          * @event click
32023          * The raw click event for the entire grid.
32024          * @param {Roo.bootstrap.NavProgressItem} this
32025          * @param {Roo.EventObject} e
32026          */
32027         "click" : true
32028     });
32029    
32030 };
32031
32032 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32033     
32034     rid : '',
32035     active : false,
32036     disabled : false,
32037     html : '',
32038     position : 'bottom',
32039     icon : false,
32040     
32041     getAutoCreate : function()
32042     {
32043         var iconCls = 'roo-navigation-bar-item-icon';
32044         
32045         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32046         
32047         var cfg = {
32048             tag: 'li',
32049             cls: 'roo-navigation-bar-item',
32050             cn : [
32051                 {
32052                     tag : 'i',
32053                     cls : iconCls
32054                 }
32055             ]
32056         };
32057         
32058         if(this.active){
32059             cfg.cls += ' active';
32060         }
32061         if(this.disabled){
32062             cfg.cls += ' disabled';
32063         }
32064         
32065         return cfg;
32066     },
32067     
32068     disable : function()
32069     {
32070         this.setDisabled(true);
32071     },
32072     
32073     enable : function()
32074     {
32075         this.setDisabled(false);
32076     },
32077     
32078     initEvents: function() 
32079     {
32080         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32081         
32082         this.iconEl.on('click', this.onClick, this);
32083     },
32084     
32085     onClick : function(e)
32086     {
32087         e.preventDefault();
32088         
32089         if(this.disabled){
32090             return;
32091         }
32092         
32093         if(this.fireEvent('click', this, e) === false){
32094             return;
32095         };
32096         
32097         this.parent().setActiveItem(this);
32098     },
32099     
32100     isActive: function () 
32101     {
32102         return this.active;
32103     },
32104     
32105     setActive : function(state)
32106     {
32107         if(this.active == state){
32108             return;
32109         }
32110         
32111         this.active = state;
32112         
32113         if (state) {
32114             this.el.addClass('active');
32115             return;
32116         }
32117         
32118         this.el.removeClass('active');
32119         
32120         return;
32121     },
32122     
32123     setDisabled : function(state)
32124     {
32125         if(this.disabled == state){
32126             return;
32127         }
32128         
32129         this.disabled = state;
32130         
32131         if (state) {
32132             this.el.addClass('disabled');
32133             return;
32134         }
32135         
32136         this.el.removeClass('disabled');
32137     },
32138     
32139     tooltipEl : function()
32140     {
32141         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32142     }
32143 });
32144  
32145
32146  /*
32147  * - LGPL
32148  *
32149  * FieldLabel
32150  * 
32151  */
32152
32153 /**
32154  * @class Roo.bootstrap.FieldLabel
32155  * @extends Roo.bootstrap.Component
32156  * Bootstrap FieldLabel class
32157  * @cfg {String} html contents of the element
32158  * @cfg {String} tag tag of the element default label
32159  * @cfg {String} cls class of the element
32160  * @cfg {String} target label target 
32161  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32162  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32163  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32164  * @cfg {String} iconTooltip default "This field is required"
32165  * @cfg {String} indicatorpos (left|right) default left
32166  * 
32167  * @constructor
32168  * Create a new FieldLabel
32169  * @param {Object} config The config object
32170  */
32171
32172 Roo.bootstrap.FieldLabel = function(config){
32173     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32174     
32175     this.addEvents({
32176             /**
32177              * @event invalid
32178              * Fires after the field has been marked as invalid.
32179              * @param {Roo.form.FieldLabel} this
32180              * @param {String} msg The validation message
32181              */
32182             invalid : true,
32183             /**
32184              * @event valid
32185              * Fires after the field has been validated with no errors.
32186              * @param {Roo.form.FieldLabel} this
32187              */
32188             valid : true
32189         });
32190 };
32191
32192 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32193     
32194     tag: 'label',
32195     cls: '',
32196     html: '',
32197     target: '',
32198     allowBlank : true,
32199     invalidClass : 'has-warning',
32200     validClass : 'has-success',
32201     iconTooltip : 'This field is required',
32202     indicatorpos : 'left',
32203     
32204     getAutoCreate : function(){
32205         
32206         var cls = "";
32207         if (!this.allowBlank) {
32208             cls  = "visible";
32209         }
32210         
32211         var cfg = {
32212             tag : this.tag,
32213             cls : 'roo-bootstrap-field-label ' + this.cls,
32214             for : this.target,
32215             cn : [
32216                 {
32217                     tag : 'i',
32218                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32219                     tooltip : this.iconTooltip
32220                 },
32221                 {
32222                     tag : 'span',
32223                     html : this.html
32224                 }
32225             ] 
32226         };
32227         
32228         if(this.indicatorpos == 'right'){
32229             var cfg = {
32230                 tag : this.tag,
32231                 cls : 'roo-bootstrap-field-label ' + this.cls,
32232                 for : this.target,
32233                 cn : [
32234                     {
32235                         tag : 'span',
32236                         html : this.html
32237                     },
32238                     {
32239                         tag : 'i',
32240                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32241                         tooltip : this.iconTooltip
32242                     }
32243                 ] 
32244             };
32245         }
32246         
32247         return cfg;
32248     },
32249     
32250     initEvents: function() 
32251     {
32252         Roo.bootstrap.Element.superclass.initEvents.call(this);
32253         
32254         this.indicator = this.indicatorEl();
32255         
32256         if(this.indicator){
32257             this.indicator.removeClass('visible');
32258             this.indicator.addClass('invisible');
32259         }
32260         
32261         Roo.bootstrap.FieldLabel.register(this);
32262     },
32263     
32264     indicatorEl : function()
32265     {
32266         var indicator = this.el.select('i.roo-required-indicator',true).first();
32267         
32268         if(!indicator){
32269             return false;
32270         }
32271         
32272         return indicator;
32273         
32274     },
32275     
32276     /**
32277      * Mark this field as valid
32278      */
32279     markValid : function()
32280     {
32281         if(this.indicator){
32282             this.indicator.removeClass('visible');
32283             this.indicator.addClass('invisible');
32284         }
32285         if (Roo.bootstrap.version == 3) {
32286             this.el.removeClass(this.invalidClass);
32287             this.el.addClass(this.validClass);
32288         } else {
32289             this.el.removeClass('is-invalid');
32290             this.el.addClass('is-valid');
32291         }
32292         
32293         
32294         this.fireEvent('valid', this);
32295     },
32296     
32297     /**
32298      * Mark this field as invalid
32299      * @param {String} msg The validation message
32300      */
32301     markInvalid : function(msg)
32302     {
32303         if(this.indicator){
32304             this.indicator.removeClass('invisible');
32305             this.indicator.addClass('visible');
32306         }
32307           if (Roo.bootstrap.version == 3) {
32308             this.el.removeClass(this.validClass);
32309             this.el.addClass(this.invalidClass);
32310         } else {
32311             this.el.removeClass('is-valid');
32312             this.el.addClass('is-invalid');
32313         }
32314         
32315         
32316         this.fireEvent('invalid', this, msg);
32317     }
32318     
32319    
32320 });
32321
32322 Roo.apply(Roo.bootstrap.FieldLabel, {
32323     
32324     groups: {},
32325     
32326      /**
32327     * register a FieldLabel Group
32328     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32329     */
32330     register : function(label)
32331     {
32332         if(this.groups.hasOwnProperty(label.target)){
32333             return;
32334         }
32335      
32336         this.groups[label.target] = label;
32337         
32338     },
32339     /**
32340     * fetch a FieldLabel Group based on the target
32341     * @param {string} target
32342     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32343     */
32344     get: function(target) {
32345         if (typeof(this.groups[target]) == 'undefined') {
32346             return false;
32347         }
32348         
32349         return this.groups[target] ;
32350     }
32351 });
32352
32353  
32354
32355  /*
32356  * - LGPL
32357  *
32358  * page DateSplitField.
32359  * 
32360  */
32361
32362
32363 /**
32364  * @class Roo.bootstrap.DateSplitField
32365  * @extends Roo.bootstrap.Component
32366  * Bootstrap DateSplitField class
32367  * @cfg {string} fieldLabel - the label associated
32368  * @cfg {Number} labelWidth set the width of label (0-12)
32369  * @cfg {String} labelAlign (top|left)
32370  * @cfg {Boolean} dayAllowBlank (true|false) default false
32371  * @cfg {Boolean} monthAllowBlank (true|false) default false
32372  * @cfg {Boolean} yearAllowBlank (true|false) default false
32373  * @cfg {string} dayPlaceholder 
32374  * @cfg {string} monthPlaceholder
32375  * @cfg {string} yearPlaceholder
32376  * @cfg {string} dayFormat default 'd'
32377  * @cfg {string} monthFormat default 'm'
32378  * @cfg {string} yearFormat default 'Y'
32379  * @cfg {Number} labellg set the width of label (1-12)
32380  * @cfg {Number} labelmd set the width of label (1-12)
32381  * @cfg {Number} labelsm set the width of label (1-12)
32382  * @cfg {Number} labelxs set the width of label (1-12)
32383
32384  *     
32385  * @constructor
32386  * Create a new DateSplitField
32387  * @param {Object} config The config object
32388  */
32389
32390 Roo.bootstrap.DateSplitField = function(config){
32391     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32392     
32393     this.addEvents({
32394         // raw events
32395          /**
32396          * @event years
32397          * getting the data of years
32398          * @param {Roo.bootstrap.DateSplitField} this
32399          * @param {Object} years
32400          */
32401         "years" : true,
32402         /**
32403          * @event days
32404          * getting the data of days
32405          * @param {Roo.bootstrap.DateSplitField} this
32406          * @param {Object} days
32407          */
32408         "days" : true,
32409         /**
32410          * @event invalid
32411          * Fires after the field has been marked as invalid.
32412          * @param {Roo.form.Field} this
32413          * @param {String} msg The validation message
32414          */
32415         invalid : true,
32416        /**
32417          * @event valid
32418          * Fires after the field has been validated with no errors.
32419          * @param {Roo.form.Field} this
32420          */
32421         valid : true
32422     });
32423 };
32424
32425 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32426     
32427     fieldLabel : '',
32428     labelAlign : 'top',
32429     labelWidth : 3,
32430     dayAllowBlank : false,
32431     monthAllowBlank : false,
32432     yearAllowBlank : false,
32433     dayPlaceholder : '',
32434     monthPlaceholder : '',
32435     yearPlaceholder : '',
32436     dayFormat : 'd',
32437     monthFormat : 'm',
32438     yearFormat : 'Y',
32439     isFormField : true,
32440     labellg : 0,
32441     labelmd : 0,
32442     labelsm : 0,
32443     labelxs : 0,
32444     
32445     getAutoCreate : function()
32446     {
32447         var cfg = {
32448             tag : 'div',
32449             cls : 'row roo-date-split-field-group',
32450             cn : [
32451                 {
32452                     tag : 'input',
32453                     type : 'hidden',
32454                     cls : 'form-hidden-field roo-date-split-field-group-value',
32455                     name : this.name
32456                 }
32457             ]
32458         };
32459         
32460         var labelCls = 'col-md-12';
32461         var contentCls = 'col-md-4';
32462         
32463         if(this.fieldLabel){
32464             
32465             var label = {
32466                 tag : 'div',
32467                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32468                 cn : [
32469                     {
32470                         tag : 'label',
32471                         html : this.fieldLabel
32472                     }
32473                 ]
32474             };
32475             
32476             if(this.labelAlign == 'left'){
32477             
32478                 if(this.labelWidth > 12){
32479                     label.style = "width: " + this.labelWidth + 'px';
32480                 }
32481
32482                 if(this.labelWidth < 13 && this.labelmd == 0){
32483                     this.labelmd = this.labelWidth;
32484                 }
32485
32486                 if(this.labellg > 0){
32487                     labelCls = ' col-lg-' + this.labellg;
32488                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32489                 }
32490
32491                 if(this.labelmd > 0){
32492                     labelCls = ' col-md-' + this.labelmd;
32493                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32494                 }
32495
32496                 if(this.labelsm > 0){
32497                     labelCls = ' col-sm-' + this.labelsm;
32498                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32499                 }
32500
32501                 if(this.labelxs > 0){
32502                     labelCls = ' col-xs-' + this.labelxs;
32503                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32504                 }
32505             }
32506             
32507             label.cls += ' ' + labelCls;
32508             
32509             cfg.cn.push(label);
32510         }
32511         
32512         Roo.each(['day', 'month', 'year'], function(t){
32513             cfg.cn.push({
32514                 tag : 'div',
32515                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32516             });
32517         }, this);
32518         
32519         return cfg;
32520     },
32521     
32522     inputEl: function ()
32523     {
32524         return this.el.select('.roo-date-split-field-group-value', true).first();
32525     },
32526     
32527     onRender : function(ct, position) 
32528     {
32529         var _this = this;
32530         
32531         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32532         
32533         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32534         
32535         this.dayField = new Roo.bootstrap.ComboBox({
32536             allowBlank : this.dayAllowBlank,
32537             alwaysQuery : true,
32538             displayField : 'value',
32539             editable : false,
32540             fieldLabel : '',
32541             forceSelection : true,
32542             mode : 'local',
32543             placeholder : this.dayPlaceholder,
32544             selectOnFocus : true,
32545             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32546             triggerAction : 'all',
32547             typeAhead : true,
32548             valueField : 'value',
32549             store : new Roo.data.SimpleStore({
32550                 data : (function() {    
32551                     var days = [];
32552                     _this.fireEvent('days', _this, days);
32553                     return days;
32554                 })(),
32555                 fields : [ 'value' ]
32556             }),
32557             listeners : {
32558                 select : function (_self, record, index)
32559                 {
32560                     _this.setValue(_this.getValue());
32561                 }
32562             }
32563         });
32564
32565         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32566         
32567         this.monthField = new Roo.bootstrap.MonthField({
32568             after : '<i class=\"fa fa-calendar\"></i>',
32569             allowBlank : this.monthAllowBlank,
32570             placeholder : this.monthPlaceholder,
32571             readOnly : true,
32572             listeners : {
32573                 render : function (_self)
32574                 {
32575                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32576                         e.preventDefault();
32577                         _self.focus();
32578                     });
32579                 },
32580                 select : function (_self, oldvalue, newvalue)
32581                 {
32582                     _this.setValue(_this.getValue());
32583                 }
32584             }
32585         });
32586         
32587         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32588         
32589         this.yearField = new Roo.bootstrap.ComboBox({
32590             allowBlank : this.yearAllowBlank,
32591             alwaysQuery : true,
32592             displayField : 'value',
32593             editable : false,
32594             fieldLabel : '',
32595             forceSelection : true,
32596             mode : 'local',
32597             placeholder : this.yearPlaceholder,
32598             selectOnFocus : true,
32599             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32600             triggerAction : 'all',
32601             typeAhead : true,
32602             valueField : 'value',
32603             store : new Roo.data.SimpleStore({
32604                 data : (function() {
32605                     var years = [];
32606                     _this.fireEvent('years', _this, years);
32607                     return years;
32608                 })(),
32609                 fields : [ 'value' ]
32610             }),
32611             listeners : {
32612                 select : function (_self, record, index)
32613                 {
32614                     _this.setValue(_this.getValue());
32615                 }
32616             }
32617         });
32618
32619         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32620     },
32621     
32622     setValue : function(v, format)
32623     {
32624         this.inputEl.dom.value = v;
32625         
32626         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32627         
32628         var d = Date.parseDate(v, f);
32629         
32630         if(!d){
32631             this.validate();
32632             return;
32633         }
32634         
32635         this.setDay(d.format(this.dayFormat));
32636         this.setMonth(d.format(this.monthFormat));
32637         this.setYear(d.format(this.yearFormat));
32638         
32639         this.validate();
32640         
32641         return;
32642     },
32643     
32644     setDay : function(v)
32645     {
32646         this.dayField.setValue(v);
32647         this.inputEl.dom.value = this.getValue();
32648         this.validate();
32649         return;
32650     },
32651     
32652     setMonth : function(v)
32653     {
32654         this.monthField.setValue(v, true);
32655         this.inputEl.dom.value = this.getValue();
32656         this.validate();
32657         return;
32658     },
32659     
32660     setYear : function(v)
32661     {
32662         this.yearField.setValue(v);
32663         this.inputEl.dom.value = this.getValue();
32664         this.validate();
32665         return;
32666     },
32667     
32668     getDay : function()
32669     {
32670         return this.dayField.getValue();
32671     },
32672     
32673     getMonth : function()
32674     {
32675         return this.monthField.getValue();
32676     },
32677     
32678     getYear : function()
32679     {
32680         return this.yearField.getValue();
32681     },
32682     
32683     getValue : function()
32684     {
32685         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32686         
32687         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32688         
32689         return date;
32690     },
32691     
32692     reset : function()
32693     {
32694         this.setDay('');
32695         this.setMonth('');
32696         this.setYear('');
32697         this.inputEl.dom.value = '';
32698         this.validate();
32699         return;
32700     },
32701     
32702     validate : function()
32703     {
32704         var d = this.dayField.validate();
32705         var m = this.monthField.validate();
32706         var y = this.yearField.validate();
32707         
32708         var valid = true;
32709         
32710         if(
32711                 (!this.dayAllowBlank && !d) ||
32712                 (!this.monthAllowBlank && !m) ||
32713                 (!this.yearAllowBlank && !y)
32714         ){
32715             valid = false;
32716         }
32717         
32718         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32719             return valid;
32720         }
32721         
32722         if(valid){
32723             this.markValid();
32724             return valid;
32725         }
32726         
32727         this.markInvalid();
32728         
32729         return valid;
32730     },
32731     
32732     markValid : function()
32733     {
32734         
32735         var label = this.el.select('label', true).first();
32736         var icon = this.el.select('i.fa-star', true).first();
32737
32738         if(label && icon){
32739             icon.remove();
32740         }
32741         
32742         this.fireEvent('valid', this);
32743     },
32744     
32745      /**
32746      * Mark this field as invalid
32747      * @param {String} msg The validation message
32748      */
32749     markInvalid : function(msg)
32750     {
32751         
32752         var label = this.el.select('label', true).first();
32753         var icon = this.el.select('i.fa-star', true).first();
32754
32755         if(label && !icon){
32756             this.el.select('.roo-date-split-field-label', true).createChild({
32757                 tag : 'i',
32758                 cls : 'text-danger fa fa-lg fa-star',
32759                 tooltip : 'This field is required',
32760                 style : 'margin-right:5px;'
32761             }, label, true);
32762         }
32763         
32764         this.fireEvent('invalid', this, msg);
32765     },
32766     
32767     clearInvalid : function()
32768     {
32769         var label = this.el.select('label', true).first();
32770         var icon = this.el.select('i.fa-star', true).first();
32771
32772         if(label && icon){
32773             icon.remove();
32774         }
32775         
32776         this.fireEvent('valid', this);
32777     },
32778     
32779     getName: function()
32780     {
32781         return this.name;
32782     }
32783     
32784 });
32785
32786  /**
32787  *
32788  * This is based on 
32789  * http://masonry.desandro.com
32790  *
32791  * The idea is to render all the bricks based on vertical width...
32792  *
32793  * The original code extends 'outlayer' - we might need to use that....
32794  * 
32795  */
32796
32797
32798 /**
32799  * @class Roo.bootstrap.LayoutMasonry
32800  * @extends Roo.bootstrap.Component
32801  * Bootstrap Layout Masonry class
32802  * 
32803  * @constructor
32804  * Create a new Element
32805  * @param {Object} config The config object
32806  */
32807
32808 Roo.bootstrap.LayoutMasonry = function(config){
32809     
32810     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32811     
32812     this.bricks = [];
32813     
32814     Roo.bootstrap.LayoutMasonry.register(this);
32815     
32816     this.addEvents({
32817         // raw events
32818         /**
32819          * @event layout
32820          * Fire after layout the items
32821          * @param {Roo.bootstrap.LayoutMasonry} this
32822          * @param {Roo.EventObject} e
32823          */
32824         "layout" : true
32825     });
32826     
32827 };
32828
32829 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32830     
32831     /**
32832      * @cfg {Boolean} isLayoutInstant = no animation?
32833      */   
32834     isLayoutInstant : false, // needed?
32835    
32836     /**
32837      * @cfg {Number} boxWidth  width of the columns
32838      */   
32839     boxWidth : 450,
32840     
32841       /**
32842      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32843      */   
32844     boxHeight : 0,
32845     
32846     /**
32847      * @cfg {Number} padWidth padding below box..
32848      */   
32849     padWidth : 10, 
32850     
32851     /**
32852      * @cfg {Number} gutter gutter width..
32853      */   
32854     gutter : 10,
32855     
32856      /**
32857      * @cfg {Number} maxCols maximum number of columns
32858      */   
32859     
32860     maxCols: 0,
32861     
32862     /**
32863      * @cfg {Boolean} isAutoInitial defalut true
32864      */   
32865     isAutoInitial : true, 
32866     
32867     containerWidth: 0,
32868     
32869     /**
32870      * @cfg {Boolean} isHorizontal defalut false
32871      */   
32872     isHorizontal : false, 
32873
32874     currentSize : null,
32875     
32876     tag: 'div',
32877     
32878     cls: '',
32879     
32880     bricks: null, //CompositeElement
32881     
32882     cols : 1,
32883     
32884     _isLayoutInited : false,
32885     
32886 //    isAlternative : false, // only use for vertical layout...
32887     
32888     /**
32889      * @cfg {Number} alternativePadWidth padding below box..
32890      */   
32891     alternativePadWidth : 50,
32892     
32893     selectedBrick : [],
32894     
32895     getAutoCreate : function(){
32896         
32897         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32898         
32899         var cfg = {
32900             tag: this.tag,
32901             cls: 'blog-masonary-wrapper ' + this.cls,
32902             cn : {
32903                 cls : 'mas-boxes masonary'
32904             }
32905         };
32906         
32907         return cfg;
32908     },
32909     
32910     getChildContainer: function( )
32911     {
32912         if (this.boxesEl) {
32913             return this.boxesEl;
32914         }
32915         
32916         this.boxesEl = this.el.select('.mas-boxes').first();
32917         
32918         return this.boxesEl;
32919     },
32920     
32921     
32922     initEvents : function()
32923     {
32924         var _this = this;
32925         
32926         if(this.isAutoInitial){
32927             Roo.log('hook children rendered');
32928             this.on('childrenrendered', function() {
32929                 Roo.log('children rendered');
32930                 _this.initial();
32931             } ,this);
32932         }
32933     },
32934     
32935     initial : function()
32936     {
32937         this.selectedBrick = [];
32938         
32939         this.currentSize = this.el.getBox(true);
32940         
32941         Roo.EventManager.onWindowResize(this.resize, this); 
32942
32943         if(!this.isAutoInitial){
32944             this.layout();
32945             return;
32946         }
32947         
32948         this.layout();
32949         
32950         return;
32951         //this.layout.defer(500,this);
32952         
32953     },
32954     
32955     resize : function()
32956     {
32957         var cs = this.el.getBox(true);
32958         
32959         if (
32960                 this.currentSize.width == cs.width && 
32961                 this.currentSize.x == cs.x && 
32962                 this.currentSize.height == cs.height && 
32963                 this.currentSize.y == cs.y 
32964         ) {
32965             Roo.log("no change in with or X or Y");
32966             return;
32967         }
32968         
32969         this.currentSize = cs;
32970         
32971         this.layout();
32972         
32973     },
32974     
32975     layout : function()
32976     {   
32977         this._resetLayout();
32978         
32979         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32980         
32981         this.layoutItems( isInstant );
32982       
32983         this._isLayoutInited = true;
32984         
32985         this.fireEvent('layout', this);
32986         
32987     },
32988     
32989     _resetLayout : function()
32990     {
32991         if(this.isHorizontal){
32992             this.horizontalMeasureColumns();
32993             return;
32994         }
32995         
32996         this.verticalMeasureColumns();
32997         
32998     },
32999     
33000     verticalMeasureColumns : function()
33001     {
33002         this.getContainerWidth();
33003         
33004 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33005 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33006 //            return;
33007 //        }
33008         
33009         var boxWidth = this.boxWidth + this.padWidth;
33010         
33011         if(this.containerWidth < this.boxWidth){
33012             boxWidth = this.containerWidth
33013         }
33014         
33015         var containerWidth = this.containerWidth;
33016         
33017         var cols = Math.floor(containerWidth / boxWidth);
33018         
33019         this.cols = Math.max( cols, 1 );
33020         
33021         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33022         
33023         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33024         
33025         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33026         
33027         this.colWidth = boxWidth + avail - this.padWidth;
33028         
33029         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33030         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33031     },
33032     
33033     horizontalMeasureColumns : function()
33034     {
33035         this.getContainerWidth();
33036         
33037         var boxWidth = this.boxWidth;
33038         
33039         if(this.containerWidth < boxWidth){
33040             boxWidth = this.containerWidth;
33041         }
33042         
33043         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33044         
33045         this.el.setHeight(boxWidth);
33046         
33047     },
33048     
33049     getContainerWidth : function()
33050     {
33051         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33052     },
33053     
33054     layoutItems : function( isInstant )
33055     {
33056         Roo.log(this.bricks);
33057         
33058         var items = Roo.apply([], this.bricks);
33059         
33060         if(this.isHorizontal){
33061             this._horizontalLayoutItems( items , isInstant );
33062             return;
33063         }
33064         
33065 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33066 //            this._verticalAlternativeLayoutItems( items , isInstant );
33067 //            return;
33068 //        }
33069         
33070         this._verticalLayoutItems( items , isInstant );
33071         
33072     },
33073     
33074     _verticalLayoutItems : function ( items , isInstant)
33075     {
33076         if ( !items || !items.length ) {
33077             return;
33078         }
33079         
33080         var standard = [
33081             ['xs', 'xs', 'xs', 'tall'],
33082             ['xs', 'xs', 'tall'],
33083             ['xs', 'xs', 'sm'],
33084             ['xs', 'xs', 'xs'],
33085             ['xs', 'tall'],
33086             ['xs', 'sm'],
33087             ['xs', 'xs'],
33088             ['xs'],
33089             
33090             ['sm', 'xs', 'xs'],
33091             ['sm', 'xs'],
33092             ['sm'],
33093             
33094             ['tall', 'xs', 'xs', 'xs'],
33095             ['tall', 'xs', 'xs'],
33096             ['tall', 'xs'],
33097             ['tall']
33098             
33099         ];
33100         
33101         var queue = [];
33102         
33103         var boxes = [];
33104         
33105         var box = [];
33106         
33107         Roo.each(items, function(item, k){
33108             
33109             switch (item.size) {
33110                 // these layouts take up a full box,
33111                 case 'md' :
33112                 case 'md-left' :
33113                 case 'md-right' :
33114                 case 'wide' :
33115                     
33116                     if(box.length){
33117                         boxes.push(box);
33118                         box = [];
33119                     }
33120                     
33121                     boxes.push([item]);
33122                     
33123                     break;
33124                     
33125                 case 'xs' :
33126                 case 'sm' :
33127                 case 'tall' :
33128                     
33129                     box.push(item);
33130                     
33131                     break;
33132                 default :
33133                     break;
33134                     
33135             }
33136             
33137         }, this);
33138         
33139         if(box.length){
33140             boxes.push(box);
33141             box = [];
33142         }
33143         
33144         var filterPattern = function(box, length)
33145         {
33146             if(!box.length){
33147                 return;
33148             }
33149             
33150             var match = false;
33151             
33152             var pattern = box.slice(0, length);
33153             
33154             var format = [];
33155             
33156             Roo.each(pattern, function(i){
33157                 format.push(i.size);
33158             }, this);
33159             
33160             Roo.each(standard, function(s){
33161                 
33162                 if(String(s) != String(format)){
33163                     return;
33164                 }
33165                 
33166                 match = true;
33167                 return false;
33168                 
33169             }, this);
33170             
33171             if(!match && length == 1){
33172                 return;
33173             }
33174             
33175             if(!match){
33176                 filterPattern(box, length - 1);
33177                 return;
33178             }
33179                 
33180             queue.push(pattern);
33181
33182             box = box.slice(length, box.length);
33183
33184             filterPattern(box, 4);
33185
33186             return;
33187             
33188         }
33189         
33190         Roo.each(boxes, function(box, k){
33191             
33192             if(!box.length){
33193                 return;
33194             }
33195             
33196             if(box.length == 1){
33197                 queue.push(box);
33198                 return;
33199             }
33200             
33201             filterPattern(box, 4);
33202             
33203         }, this);
33204         
33205         this._processVerticalLayoutQueue( queue, isInstant );
33206         
33207     },
33208     
33209 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33210 //    {
33211 //        if ( !items || !items.length ) {
33212 //            return;
33213 //        }
33214 //
33215 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33216 //        
33217 //    },
33218     
33219     _horizontalLayoutItems : function ( items , isInstant)
33220     {
33221         if ( !items || !items.length || items.length < 3) {
33222             return;
33223         }
33224         
33225         items.reverse();
33226         
33227         var eItems = items.slice(0, 3);
33228         
33229         items = items.slice(3, items.length);
33230         
33231         var standard = [
33232             ['xs', 'xs', 'xs', 'wide'],
33233             ['xs', 'xs', 'wide'],
33234             ['xs', 'xs', 'sm'],
33235             ['xs', 'xs', 'xs'],
33236             ['xs', 'wide'],
33237             ['xs', 'sm'],
33238             ['xs', 'xs'],
33239             ['xs'],
33240             
33241             ['sm', 'xs', 'xs'],
33242             ['sm', 'xs'],
33243             ['sm'],
33244             
33245             ['wide', 'xs', 'xs', 'xs'],
33246             ['wide', 'xs', 'xs'],
33247             ['wide', 'xs'],
33248             ['wide'],
33249             
33250             ['wide-thin']
33251         ];
33252         
33253         var queue = [];
33254         
33255         var boxes = [];
33256         
33257         var box = [];
33258         
33259         Roo.each(items, function(item, k){
33260             
33261             switch (item.size) {
33262                 case 'md' :
33263                 case 'md-left' :
33264                 case 'md-right' :
33265                 case 'tall' :
33266                     
33267                     if(box.length){
33268                         boxes.push(box);
33269                         box = [];
33270                     }
33271                     
33272                     boxes.push([item]);
33273                     
33274                     break;
33275                     
33276                 case 'xs' :
33277                 case 'sm' :
33278                 case 'wide' :
33279                 case 'wide-thin' :
33280                     
33281                     box.push(item);
33282                     
33283                     break;
33284                 default :
33285                     break;
33286                     
33287             }
33288             
33289         }, this);
33290         
33291         if(box.length){
33292             boxes.push(box);
33293             box = [];
33294         }
33295         
33296         var filterPattern = function(box, length)
33297         {
33298             if(!box.length){
33299                 return;
33300             }
33301             
33302             var match = false;
33303             
33304             var pattern = box.slice(0, length);
33305             
33306             var format = [];
33307             
33308             Roo.each(pattern, function(i){
33309                 format.push(i.size);
33310             }, this);
33311             
33312             Roo.each(standard, function(s){
33313                 
33314                 if(String(s) != String(format)){
33315                     return;
33316                 }
33317                 
33318                 match = true;
33319                 return false;
33320                 
33321             }, this);
33322             
33323             if(!match && length == 1){
33324                 return;
33325             }
33326             
33327             if(!match){
33328                 filterPattern(box, length - 1);
33329                 return;
33330             }
33331                 
33332             queue.push(pattern);
33333
33334             box = box.slice(length, box.length);
33335
33336             filterPattern(box, 4);
33337
33338             return;
33339             
33340         }
33341         
33342         Roo.each(boxes, function(box, k){
33343             
33344             if(!box.length){
33345                 return;
33346             }
33347             
33348             if(box.length == 1){
33349                 queue.push(box);
33350                 return;
33351             }
33352             
33353             filterPattern(box, 4);
33354             
33355         }, this);
33356         
33357         
33358         var prune = [];
33359         
33360         var pos = this.el.getBox(true);
33361         
33362         var minX = pos.x;
33363         
33364         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33365         
33366         var hit_end = false;
33367         
33368         Roo.each(queue, function(box){
33369             
33370             if(hit_end){
33371                 
33372                 Roo.each(box, function(b){
33373                 
33374                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33375                     b.el.hide();
33376
33377                 }, this);
33378
33379                 return;
33380             }
33381             
33382             var mx = 0;
33383             
33384             Roo.each(box, function(b){
33385                 
33386                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33387                 b.el.show();
33388
33389                 mx = Math.max(mx, b.x);
33390                 
33391             }, this);
33392             
33393             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33394             
33395             if(maxX < minX){
33396                 
33397                 Roo.each(box, function(b){
33398                 
33399                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33400                     b.el.hide();
33401                     
33402                 }, this);
33403                 
33404                 hit_end = true;
33405                 
33406                 return;
33407             }
33408             
33409             prune.push(box);
33410             
33411         }, this);
33412         
33413         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33414     },
33415     
33416     /** Sets position of item in DOM
33417     * @param {Element} item
33418     * @param {Number} x - horizontal position
33419     * @param {Number} y - vertical position
33420     * @param {Boolean} isInstant - disables transitions
33421     */
33422     _processVerticalLayoutQueue : function( queue, isInstant )
33423     {
33424         var pos = this.el.getBox(true);
33425         var x = pos.x;
33426         var y = pos.y;
33427         var maxY = [];
33428         
33429         for (var i = 0; i < this.cols; i++){
33430             maxY[i] = pos.y;
33431         }
33432         
33433         Roo.each(queue, function(box, k){
33434             
33435             var col = k % this.cols;
33436             
33437             Roo.each(box, function(b,kk){
33438                 
33439                 b.el.position('absolute');
33440                 
33441                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33442                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33443                 
33444                 if(b.size == 'md-left' || b.size == 'md-right'){
33445                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33446                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33447                 }
33448                 
33449                 b.el.setWidth(width);
33450                 b.el.setHeight(height);
33451                 // iframe?
33452                 b.el.select('iframe',true).setSize(width,height);
33453                 
33454             }, this);
33455             
33456             for (var i = 0; i < this.cols; i++){
33457                 
33458                 if(maxY[i] < maxY[col]){
33459                     col = i;
33460                     continue;
33461                 }
33462                 
33463                 col = Math.min(col, i);
33464                 
33465             }
33466             
33467             x = pos.x + col * (this.colWidth + this.padWidth);
33468             
33469             y = maxY[col];
33470             
33471             var positions = [];
33472             
33473             switch (box.length){
33474                 case 1 :
33475                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33476                     break;
33477                 case 2 :
33478                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33479                     break;
33480                 case 3 :
33481                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33482                     break;
33483                 case 4 :
33484                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33485                     break;
33486                 default :
33487                     break;
33488             }
33489             
33490             Roo.each(box, function(b,kk){
33491                 
33492                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33493                 
33494                 var sz = b.el.getSize();
33495                 
33496                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33497                 
33498             }, this);
33499             
33500         }, this);
33501         
33502         var mY = 0;
33503         
33504         for (var i = 0; i < this.cols; i++){
33505             mY = Math.max(mY, maxY[i]);
33506         }
33507         
33508         this.el.setHeight(mY - pos.y);
33509         
33510     },
33511     
33512 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33513 //    {
33514 //        var pos = this.el.getBox(true);
33515 //        var x = pos.x;
33516 //        var y = pos.y;
33517 //        var maxX = pos.right;
33518 //        
33519 //        var maxHeight = 0;
33520 //        
33521 //        Roo.each(items, function(item, k){
33522 //            
33523 //            var c = k % 2;
33524 //            
33525 //            item.el.position('absolute');
33526 //                
33527 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33528 //
33529 //            item.el.setWidth(width);
33530 //
33531 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33532 //
33533 //            item.el.setHeight(height);
33534 //            
33535 //            if(c == 0){
33536 //                item.el.setXY([x, y], isInstant ? false : true);
33537 //            } else {
33538 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33539 //            }
33540 //            
33541 //            y = y + height + this.alternativePadWidth;
33542 //            
33543 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33544 //            
33545 //        }, this);
33546 //        
33547 //        this.el.setHeight(maxHeight);
33548 //        
33549 //    },
33550     
33551     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33552     {
33553         var pos = this.el.getBox(true);
33554         
33555         var minX = pos.x;
33556         var minY = pos.y;
33557         
33558         var maxX = pos.right;
33559         
33560         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33561         
33562         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33563         
33564         Roo.each(queue, function(box, k){
33565             
33566             Roo.each(box, function(b, kk){
33567                 
33568                 b.el.position('absolute');
33569                 
33570                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33571                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33572                 
33573                 if(b.size == 'md-left' || b.size == 'md-right'){
33574                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33575                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33576                 }
33577                 
33578                 b.el.setWidth(width);
33579                 b.el.setHeight(height);
33580                 
33581             }, this);
33582             
33583             if(!box.length){
33584                 return;
33585             }
33586             
33587             var positions = [];
33588             
33589             switch (box.length){
33590                 case 1 :
33591                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33592                     break;
33593                 case 2 :
33594                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33595                     break;
33596                 case 3 :
33597                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33598                     break;
33599                 case 4 :
33600                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33601                     break;
33602                 default :
33603                     break;
33604             }
33605             
33606             Roo.each(box, function(b,kk){
33607                 
33608                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33609                 
33610                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33611                 
33612             }, this);
33613             
33614         }, this);
33615         
33616     },
33617     
33618     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33619     {
33620         Roo.each(eItems, function(b,k){
33621             
33622             b.size = (k == 0) ? 'sm' : 'xs';
33623             b.x = (k == 0) ? 2 : 1;
33624             b.y = (k == 0) ? 2 : 1;
33625             
33626             b.el.position('absolute');
33627             
33628             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33629                 
33630             b.el.setWidth(width);
33631             
33632             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33633             
33634             b.el.setHeight(height);
33635             
33636         }, this);
33637
33638         var positions = [];
33639         
33640         positions.push({
33641             x : maxX - this.unitWidth * 2 - this.gutter,
33642             y : minY
33643         });
33644         
33645         positions.push({
33646             x : maxX - this.unitWidth,
33647             y : minY + (this.unitWidth + this.gutter) * 2
33648         });
33649         
33650         positions.push({
33651             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33652             y : minY
33653         });
33654         
33655         Roo.each(eItems, function(b,k){
33656             
33657             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33658
33659         }, this);
33660         
33661     },
33662     
33663     getVerticalOneBoxColPositions : function(x, y, box)
33664     {
33665         var pos = [];
33666         
33667         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33668         
33669         if(box[0].size == 'md-left'){
33670             rand = 0;
33671         }
33672         
33673         if(box[0].size == 'md-right'){
33674             rand = 1;
33675         }
33676         
33677         pos.push({
33678             x : x + (this.unitWidth + this.gutter) * rand,
33679             y : y
33680         });
33681         
33682         return pos;
33683     },
33684     
33685     getVerticalTwoBoxColPositions : function(x, y, box)
33686     {
33687         var pos = [];
33688         
33689         if(box[0].size == 'xs'){
33690             
33691             pos.push({
33692                 x : x,
33693                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33694             });
33695
33696             pos.push({
33697                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33698                 y : y
33699             });
33700             
33701             return pos;
33702             
33703         }
33704         
33705         pos.push({
33706             x : x,
33707             y : y
33708         });
33709
33710         pos.push({
33711             x : x + (this.unitWidth + this.gutter) * 2,
33712             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33713         });
33714         
33715         return pos;
33716         
33717     },
33718     
33719     getVerticalThreeBoxColPositions : function(x, y, box)
33720     {
33721         var pos = [];
33722         
33723         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33724             
33725             pos.push({
33726                 x : x,
33727                 y : y
33728             });
33729
33730             pos.push({
33731                 x : x + (this.unitWidth + this.gutter) * 1,
33732                 y : y
33733             });
33734             
33735             pos.push({
33736                 x : x + (this.unitWidth + this.gutter) * 2,
33737                 y : y
33738             });
33739             
33740             return pos;
33741             
33742         }
33743         
33744         if(box[0].size == 'xs' && box[1].size == 'xs'){
33745             
33746             pos.push({
33747                 x : x,
33748                 y : y
33749             });
33750
33751             pos.push({
33752                 x : x,
33753                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33754             });
33755             
33756             pos.push({
33757                 x : x + (this.unitWidth + this.gutter) * 1,
33758                 y : y
33759             });
33760             
33761             return pos;
33762             
33763         }
33764         
33765         pos.push({
33766             x : x,
33767             y : y
33768         });
33769
33770         pos.push({
33771             x : x + (this.unitWidth + this.gutter) * 2,
33772             y : y
33773         });
33774
33775         pos.push({
33776             x : x + (this.unitWidth + this.gutter) * 2,
33777             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33778         });
33779             
33780         return pos;
33781         
33782     },
33783     
33784     getVerticalFourBoxColPositions : function(x, y, box)
33785     {
33786         var pos = [];
33787         
33788         if(box[0].size == 'xs'){
33789             
33790             pos.push({
33791                 x : x,
33792                 y : y
33793             });
33794
33795             pos.push({
33796                 x : x,
33797                 y : y + (this.unitHeight + this.gutter) * 1
33798             });
33799             
33800             pos.push({
33801                 x : x,
33802                 y : y + (this.unitHeight + this.gutter) * 2
33803             });
33804             
33805             pos.push({
33806                 x : x + (this.unitWidth + this.gutter) * 1,
33807                 y : y
33808             });
33809             
33810             return pos;
33811             
33812         }
33813         
33814         pos.push({
33815             x : x,
33816             y : y
33817         });
33818
33819         pos.push({
33820             x : x + (this.unitWidth + this.gutter) * 2,
33821             y : y
33822         });
33823
33824         pos.push({
33825             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33826             y : y + (this.unitHeight + this.gutter) * 1
33827         });
33828
33829         pos.push({
33830             x : x + (this.unitWidth + this.gutter) * 2,
33831             y : y + (this.unitWidth + this.gutter) * 2
33832         });
33833
33834         return pos;
33835         
33836     },
33837     
33838     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33839     {
33840         var pos = [];
33841         
33842         if(box[0].size == 'md-left'){
33843             pos.push({
33844                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33845                 y : minY
33846             });
33847             
33848             return pos;
33849         }
33850         
33851         if(box[0].size == 'md-right'){
33852             pos.push({
33853                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33854                 y : minY + (this.unitWidth + this.gutter) * 1
33855             });
33856             
33857             return pos;
33858         }
33859         
33860         var rand = Math.floor(Math.random() * (4 - box[0].y));
33861         
33862         pos.push({
33863             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33864             y : minY + (this.unitWidth + this.gutter) * rand
33865         });
33866         
33867         return pos;
33868         
33869     },
33870     
33871     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33872     {
33873         var pos = [];
33874         
33875         if(box[0].size == 'xs'){
33876             
33877             pos.push({
33878                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33879                 y : minY
33880             });
33881
33882             pos.push({
33883                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33884                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33885             });
33886             
33887             return pos;
33888             
33889         }
33890         
33891         pos.push({
33892             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33893             y : minY
33894         });
33895
33896         pos.push({
33897             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33898             y : minY + (this.unitWidth + this.gutter) * 2
33899         });
33900         
33901         return pos;
33902         
33903     },
33904     
33905     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33906     {
33907         var pos = [];
33908         
33909         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33910             
33911             pos.push({
33912                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33913                 y : minY
33914             });
33915
33916             pos.push({
33917                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33918                 y : minY + (this.unitWidth + this.gutter) * 1
33919             });
33920             
33921             pos.push({
33922                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33923                 y : minY + (this.unitWidth + this.gutter) * 2
33924             });
33925             
33926             return pos;
33927             
33928         }
33929         
33930         if(box[0].size == 'xs' && box[1].size == 'xs'){
33931             
33932             pos.push({
33933                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33934                 y : minY
33935             });
33936
33937             pos.push({
33938                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33939                 y : minY
33940             });
33941             
33942             pos.push({
33943                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33944                 y : minY + (this.unitWidth + this.gutter) * 1
33945             });
33946             
33947             return pos;
33948             
33949         }
33950         
33951         pos.push({
33952             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33953             y : minY
33954         });
33955
33956         pos.push({
33957             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33958             y : minY + (this.unitWidth + this.gutter) * 2
33959         });
33960
33961         pos.push({
33962             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33963             y : minY + (this.unitWidth + this.gutter) * 2
33964         });
33965             
33966         return pos;
33967         
33968     },
33969     
33970     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33971     {
33972         var pos = [];
33973         
33974         if(box[0].size == 'xs'){
33975             
33976             pos.push({
33977                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33978                 y : minY
33979             });
33980
33981             pos.push({
33982                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33983                 y : minY
33984             });
33985             
33986             pos.push({
33987                 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),
33988                 y : minY
33989             });
33990             
33991             pos.push({
33992                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
33993                 y : minY + (this.unitWidth + this.gutter) * 1
33994             });
33995             
33996             return pos;
33997             
33998         }
33999         
34000         pos.push({
34001             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34002             y : minY
34003         });
34004         
34005         pos.push({
34006             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34007             y : minY + (this.unitWidth + this.gutter) * 2
34008         });
34009         
34010         pos.push({
34011             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34012             y : minY + (this.unitWidth + this.gutter) * 2
34013         });
34014         
34015         pos.push({
34016             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),
34017             y : minY + (this.unitWidth + this.gutter) * 2
34018         });
34019
34020         return pos;
34021         
34022     },
34023     
34024     /**
34025     * remove a Masonry Brick
34026     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34027     */
34028     removeBrick : function(brick_id)
34029     {
34030         if (!brick_id) {
34031             return;
34032         }
34033         
34034         for (var i = 0; i<this.bricks.length; i++) {
34035             if (this.bricks[i].id == brick_id) {
34036                 this.bricks.splice(i,1);
34037                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34038                 this.initial();
34039             }
34040         }
34041     },
34042     
34043     /**
34044     * adds a Masonry Brick
34045     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34046     */
34047     addBrick : function(cfg)
34048     {
34049         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34050         //this.register(cn);
34051         cn.parentId = this.id;
34052         cn.render(this.el);
34053         return cn;
34054     },
34055     
34056     /**
34057     * register a Masonry Brick
34058     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34059     */
34060     
34061     register : function(brick)
34062     {
34063         this.bricks.push(brick);
34064         brick.masonryId = this.id;
34065     },
34066     
34067     /**
34068     * clear all the Masonry Brick
34069     */
34070     clearAll : function()
34071     {
34072         this.bricks = [];
34073         //this.getChildContainer().dom.innerHTML = "";
34074         this.el.dom.innerHTML = '';
34075     },
34076     
34077     getSelected : function()
34078     {
34079         if (!this.selectedBrick) {
34080             return false;
34081         }
34082         
34083         return this.selectedBrick;
34084     }
34085 });
34086
34087 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34088     
34089     groups: {},
34090      /**
34091     * register a Masonry Layout
34092     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34093     */
34094     
34095     register : function(layout)
34096     {
34097         this.groups[layout.id] = layout;
34098     },
34099     /**
34100     * fetch a  Masonry Layout based on the masonry layout ID
34101     * @param {string} the masonry layout to add
34102     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34103     */
34104     
34105     get: function(layout_id) {
34106         if (typeof(this.groups[layout_id]) == 'undefined') {
34107             return false;
34108         }
34109         return this.groups[layout_id] ;
34110     }
34111     
34112     
34113     
34114 });
34115
34116  
34117
34118  /**
34119  *
34120  * This is based on 
34121  * http://masonry.desandro.com
34122  *
34123  * The idea is to render all the bricks based on vertical width...
34124  *
34125  * The original code extends 'outlayer' - we might need to use that....
34126  * 
34127  */
34128
34129
34130 /**
34131  * @class Roo.bootstrap.LayoutMasonryAuto
34132  * @extends Roo.bootstrap.Component
34133  * Bootstrap Layout Masonry class
34134  * 
34135  * @constructor
34136  * Create a new Element
34137  * @param {Object} config The config object
34138  */
34139
34140 Roo.bootstrap.LayoutMasonryAuto = function(config){
34141     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34142 };
34143
34144 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34145     
34146       /**
34147      * @cfg {Boolean} isFitWidth  - resize the width..
34148      */   
34149     isFitWidth : false,  // options..
34150     /**
34151      * @cfg {Boolean} isOriginLeft = left align?
34152      */   
34153     isOriginLeft : true,
34154     /**
34155      * @cfg {Boolean} isOriginTop = top align?
34156      */   
34157     isOriginTop : false,
34158     /**
34159      * @cfg {Boolean} isLayoutInstant = no animation?
34160      */   
34161     isLayoutInstant : false, // needed?
34162     /**
34163      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34164      */   
34165     isResizingContainer : true,
34166     /**
34167      * @cfg {Number} columnWidth  width of the columns 
34168      */   
34169     
34170     columnWidth : 0,
34171     
34172     /**
34173      * @cfg {Number} maxCols maximum number of columns
34174      */   
34175     
34176     maxCols: 0,
34177     /**
34178      * @cfg {Number} padHeight padding below box..
34179      */   
34180     
34181     padHeight : 10, 
34182     
34183     /**
34184      * @cfg {Boolean} isAutoInitial defalut true
34185      */   
34186     
34187     isAutoInitial : true, 
34188     
34189     // private?
34190     gutter : 0,
34191     
34192     containerWidth: 0,
34193     initialColumnWidth : 0,
34194     currentSize : null,
34195     
34196     colYs : null, // array.
34197     maxY : 0,
34198     padWidth: 10,
34199     
34200     
34201     tag: 'div',
34202     cls: '',
34203     bricks: null, //CompositeElement
34204     cols : 0, // array?
34205     // element : null, // wrapped now this.el
34206     _isLayoutInited : null, 
34207     
34208     
34209     getAutoCreate : function(){
34210         
34211         var cfg = {
34212             tag: this.tag,
34213             cls: 'blog-masonary-wrapper ' + this.cls,
34214             cn : {
34215                 cls : 'mas-boxes masonary'
34216             }
34217         };
34218         
34219         return cfg;
34220     },
34221     
34222     getChildContainer: function( )
34223     {
34224         if (this.boxesEl) {
34225             return this.boxesEl;
34226         }
34227         
34228         this.boxesEl = this.el.select('.mas-boxes').first();
34229         
34230         return this.boxesEl;
34231     },
34232     
34233     
34234     initEvents : function()
34235     {
34236         var _this = this;
34237         
34238         if(this.isAutoInitial){
34239             Roo.log('hook children rendered');
34240             this.on('childrenrendered', function() {
34241                 Roo.log('children rendered');
34242                 _this.initial();
34243             } ,this);
34244         }
34245         
34246     },
34247     
34248     initial : function()
34249     {
34250         this.reloadItems();
34251
34252         this.currentSize = this.el.getBox(true);
34253
34254         /// was window resize... - let's see if this works..
34255         Roo.EventManager.onWindowResize(this.resize, this); 
34256
34257         if(!this.isAutoInitial){
34258             this.layout();
34259             return;
34260         }
34261         
34262         this.layout.defer(500,this);
34263     },
34264     
34265     reloadItems: function()
34266     {
34267         this.bricks = this.el.select('.masonry-brick', true);
34268         
34269         this.bricks.each(function(b) {
34270             //Roo.log(b.getSize());
34271             if (!b.attr('originalwidth')) {
34272                 b.attr('originalwidth',  b.getSize().width);
34273             }
34274             
34275         });
34276         
34277         Roo.log(this.bricks.elements.length);
34278     },
34279     
34280     resize : function()
34281     {
34282         Roo.log('resize');
34283         var cs = this.el.getBox(true);
34284         
34285         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34286             Roo.log("no change in with or X");
34287             return;
34288         }
34289         this.currentSize = cs;
34290         this.layout();
34291     },
34292     
34293     layout : function()
34294     {
34295          Roo.log('layout');
34296         this._resetLayout();
34297         //this._manageStamps();
34298       
34299         // don't animate first layout
34300         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34301         this.layoutItems( isInstant );
34302       
34303         // flag for initalized
34304         this._isLayoutInited = true;
34305     },
34306     
34307     layoutItems : function( isInstant )
34308     {
34309         //var items = this._getItemsForLayout( this.items );
34310         // original code supports filtering layout items.. we just ignore it..
34311         
34312         this._layoutItems( this.bricks , isInstant );
34313       
34314         this._postLayout();
34315     },
34316     _layoutItems : function ( items , isInstant)
34317     {
34318        //this.fireEvent( 'layout', this, items );
34319     
34320
34321         if ( !items || !items.elements.length ) {
34322           // no items, emit event with empty array
34323             return;
34324         }
34325
34326         var queue = [];
34327         items.each(function(item) {
34328             Roo.log("layout item");
34329             Roo.log(item);
34330             // get x/y object from method
34331             var position = this._getItemLayoutPosition( item );
34332             // enqueue
34333             position.item = item;
34334             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34335             queue.push( position );
34336         }, this);
34337       
34338         this._processLayoutQueue( queue );
34339     },
34340     /** Sets position of item in DOM
34341     * @param {Element} item
34342     * @param {Number} x - horizontal position
34343     * @param {Number} y - vertical position
34344     * @param {Boolean} isInstant - disables transitions
34345     */
34346     _processLayoutQueue : function( queue )
34347     {
34348         for ( var i=0, len = queue.length; i < len; i++ ) {
34349             var obj = queue[i];
34350             obj.item.position('absolute');
34351             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34352         }
34353     },
34354       
34355     
34356     /**
34357     * Any logic you want to do after each layout,
34358     * i.e. size the container
34359     */
34360     _postLayout : function()
34361     {
34362         this.resizeContainer();
34363     },
34364     
34365     resizeContainer : function()
34366     {
34367         if ( !this.isResizingContainer ) {
34368             return;
34369         }
34370         var size = this._getContainerSize();
34371         if ( size ) {
34372             this.el.setSize(size.width,size.height);
34373             this.boxesEl.setSize(size.width,size.height);
34374         }
34375     },
34376     
34377     
34378     
34379     _resetLayout : function()
34380     {
34381         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34382         this.colWidth = this.el.getWidth();
34383         //this.gutter = this.el.getWidth(); 
34384         
34385         this.measureColumns();
34386
34387         // reset column Y
34388         var i = this.cols;
34389         this.colYs = [];
34390         while (i--) {
34391             this.colYs.push( 0 );
34392         }
34393     
34394         this.maxY = 0;
34395     },
34396
34397     measureColumns : function()
34398     {
34399         this.getContainerWidth();
34400       // if columnWidth is 0, default to outerWidth of first item
34401         if ( !this.columnWidth ) {
34402             var firstItem = this.bricks.first();
34403             Roo.log(firstItem);
34404             this.columnWidth  = this.containerWidth;
34405             if (firstItem && firstItem.attr('originalwidth') ) {
34406                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34407             }
34408             // columnWidth fall back to item of first element
34409             Roo.log("set column width?");
34410                         this.initialColumnWidth = this.columnWidth  ;
34411
34412             // if first elem has no width, default to size of container
34413             
34414         }
34415         
34416         
34417         if (this.initialColumnWidth) {
34418             this.columnWidth = this.initialColumnWidth;
34419         }
34420         
34421         
34422             
34423         // column width is fixed at the top - however if container width get's smaller we should
34424         // reduce it...
34425         
34426         // this bit calcs how man columns..
34427             
34428         var columnWidth = this.columnWidth += this.gutter;
34429       
34430         // calculate columns
34431         var containerWidth = this.containerWidth + this.gutter;
34432         
34433         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34434         // fix rounding errors, typically with gutters
34435         var excess = columnWidth - containerWidth % columnWidth;
34436         
34437         
34438         // if overshoot is less than a pixel, round up, otherwise floor it
34439         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34440         cols = Math[ mathMethod ]( cols );
34441         this.cols = Math.max( cols, 1 );
34442         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34443         
34444          // padding positioning..
34445         var totalColWidth = this.cols * this.columnWidth;
34446         var padavail = this.containerWidth - totalColWidth;
34447         // so for 2 columns - we need 3 'pads'
34448         
34449         var padNeeded = (1+this.cols) * this.padWidth;
34450         
34451         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34452         
34453         this.columnWidth += padExtra
34454         //this.padWidth = Math.floor(padavail /  ( this.cols));
34455         
34456         // adjust colum width so that padding is fixed??
34457         
34458         // we have 3 columns ... total = width * 3
34459         // we have X left over... that should be used by 
34460         
34461         //if (this.expandC) {
34462             
34463         //}
34464         
34465         
34466         
34467     },
34468     
34469     getContainerWidth : function()
34470     {
34471        /* // container is parent if fit width
34472         var container = this.isFitWidth ? this.element.parentNode : this.element;
34473         // check that this.size and size are there
34474         // IE8 triggers resize on body size change, so they might not be
34475         
34476         var size = getSize( container );  //FIXME
34477         this.containerWidth = size && size.innerWidth; //FIXME
34478         */
34479          
34480         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34481         
34482     },
34483     
34484     _getItemLayoutPosition : function( item )  // what is item?
34485     {
34486         // we resize the item to our columnWidth..
34487       
34488         item.setWidth(this.columnWidth);
34489         item.autoBoxAdjust  = false;
34490         
34491         var sz = item.getSize();
34492  
34493         // how many columns does this brick span
34494         var remainder = this.containerWidth % this.columnWidth;
34495         
34496         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34497         // round if off by 1 pixel, otherwise use ceil
34498         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34499         colSpan = Math.min( colSpan, this.cols );
34500         
34501         // normally this should be '1' as we dont' currently allow multi width columns..
34502         
34503         var colGroup = this._getColGroup( colSpan );
34504         // get the minimum Y value from the columns
34505         var minimumY = Math.min.apply( Math, colGroup );
34506         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34507         
34508         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34509          
34510         // position the brick
34511         var position = {
34512             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34513             y: this.currentSize.y + minimumY + this.padHeight
34514         };
34515         
34516         Roo.log(position);
34517         // apply setHeight to necessary columns
34518         var setHeight = minimumY + sz.height + this.padHeight;
34519         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34520         
34521         var setSpan = this.cols + 1 - colGroup.length;
34522         for ( var i = 0; i < setSpan; i++ ) {
34523           this.colYs[ shortColIndex + i ] = setHeight ;
34524         }
34525       
34526         return position;
34527     },
34528     
34529     /**
34530      * @param {Number} colSpan - number of columns the element spans
34531      * @returns {Array} colGroup
34532      */
34533     _getColGroup : function( colSpan )
34534     {
34535         if ( colSpan < 2 ) {
34536           // if brick spans only one column, use all the column Ys
34537           return this.colYs;
34538         }
34539       
34540         var colGroup = [];
34541         // how many different places could this brick fit horizontally
34542         var groupCount = this.cols + 1 - colSpan;
34543         // for each group potential horizontal position
34544         for ( var i = 0; i < groupCount; i++ ) {
34545           // make an array of colY values for that one group
34546           var groupColYs = this.colYs.slice( i, i + colSpan );
34547           // and get the max value of the array
34548           colGroup[i] = Math.max.apply( Math, groupColYs );
34549         }
34550         return colGroup;
34551     },
34552     /*
34553     _manageStamp : function( stamp )
34554     {
34555         var stampSize =  stamp.getSize();
34556         var offset = stamp.getBox();
34557         // get the columns that this stamp affects
34558         var firstX = this.isOriginLeft ? offset.x : offset.right;
34559         var lastX = firstX + stampSize.width;
34560         var firstCol = Math.floor( firstX / this.columnWidth );
34561         firstCol = Math.max( 0, firstCol );
34562         
34563         var lastCol = Math.floor( lastX / this.columnWidth );
34564         // lastCol should not go over if multiple of columnWidth #425
34565         lastCol -= lastX % this.columnWidth ? 0 : 1;
34566         lastCol = Math.min( this.cols - 1, lastCol );
34567         
34568         // set colYs to bottom of the stamp
34569         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34570             stampSize.height;
34571             
34572         for ( var i = firstCol; i <= lastCol; i++ ) {
34573           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34574         }
34575     },
34576     */
34577     
34578     _getContainerSize : function()
34579     {
34580         this.maxY = Math.max.apply( Math, this.colYs );
34581         var size = {
34582             height: this.maxY
34583         };
34584       
34585         if ( this.isFitWidth ) {
34586             size.width = this._getContainerFitWidth();
34587         }
34588       
34589         return size;
34590     },
34591     
34592     _getContainerFitWidth : function()
34593     {
34594         var unusedCols = 0;
34595         // count unused columns
34596         var i = this.cols;
34597         while ( --i ) {
34598           if ( this.colYs[i] !== 0 ) {
34599             break;
34600           }
34601           unusedCols++;
34602         }
34603         // fit container to columns that have been used
34604         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34605     },
34606     
34607     needsResizeLayout : function()
34608     {
34609         var previousWidth = this.containerWidth;
34610         this.getContainerWidth();
34611         return previousWidth !== this.containerWidth;
34612     }
34613  
34614 });
34615
34616  
34617
34618  /*
34619  * - LGPL
34620  *
34621  * element
34622  * 
34623  */
34624
34625 /**
34626  * @class Roo.bootstrap.MasonryBrick
34627  * @extends Roo.bootstrap.Component
34628  * Bootstrap MasonryBrick class
34629  * 
34630  * @constructor
34631  * Create a new MasonryBrick
34632  * @param {Object} config The config object
34633  */
34634
34635 Roo.bootstrap.MasonryBrick = function(config){
34636     
34637     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34638     
34639     Roo.bootstrap.MasonryBrick.register(this);
34640     
34641     this.addEvents({
34642         // raw events
34643         /**
34644          * @event click
34645          * When a MasonryBrick is clcik
34646          * @param {Roo.bootstrap.MasonryBrick} this
34647          * @param {Roo.EventObject} e
34648          */
34649         "click" : true
34650     });
34651 };
34652
34653 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34654     
34655     /**
34656      * @cfg {String} title
34657      */   
34658     title : '',
34659     /**
34660      * @cfg {String} html
34661      */   
34662     html : '',
34663     /**
34664      * @cfg {String} bgimage
34665      */   
34666     bgimage : '',
34667     /**
34668      * @cfg {String} videourl
34669      */   
34670     videourl : '',
34671     /**
34672      * @cfg {String} cls
34673      */   
34674     cls : '',
34675     /**
34676      * @cfg {String} href
34677      */   
34678     href : '',
34679     /**
34680      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34681      */   
34682     size : 'xs',
34683     
34684     /**
34685      * @cfg {String} placetitle (center|bottom)
34686      */   
34687     placetitle : '',
34688     
34689     /**
34690      * @cfg {Boolean} isFitContainer defalut true
34691      */   
34692     isFitContainer : true, 
34693     
34694     /**
34695      * @cfg {Boolean} preventDefault defalut false
34696      */   
34697     preventDefault : false, 
34698     
34699     /**
34700      * @cfg {Boolean} inverse defalut false
34701      */   
34702     maskInverse : false, 
34703     
34704     getAutoCreate : function()
34705     {
34706         if(!this.isFitContainer){
34707             return this.getSplitAutoCreate();
34708         }
34709         
34710         var cls = 'masonry-brick masonry-brick-full';
34711         
34712         if(this.href.length){
34713             cls += ' masonry-brick-link';
34714         }
34715         
34716         if(this.bgimage.length){
34717             cls += ' masonry-brick-image';
34718         }
34719         
34720         if(this.maskInverse){
34721             cls += ' mask-inverse';
34722         }
34723         
34724         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34725             cls += ' enable-mask';
34726         }
34727         
34728         if(this.size){
34729             cls += ' masonry-' + this.size + '-brick';
34730         }
34731         
34732         if(this.placetitle.length){
34733             
34734             switch (this.placetitle) {
34735                 case 'center' :
34736                     cls += ' masonry-center-title';
34737                     break;
34738                 case 'bottom' :
34739                     cls += ' masonry-bottom-title';
34740                     break;
34741                 default:
34742                     break;
34743             }
34744             
34745         } else {
34746             if(!this.html.length && !this.bgimage.length){
34747                 cls += ' masonry-center-title';
34748             }
34749
34750             if(!this.html.length && this.bgimage.length){
34751                 cls += ' masonry-bottom-title';
34752             }
34753         }
34754         
34755         if(this.cls){
34756             cls += ' ' + this.cls;
34757         }
34758         
34759         var cfg = {
34760             tag: (this.href.length) ? 'a' : 'div',
34761             cls: cls,
34762             cn: [
34763                 {
34764                     tag: 'div',
34765                     cls: 'masonry-brick-mask'
34766                 },
34767                 {
34768                     tag: 'div',
34769                     cls: 'masonry-brick-paragraph',
34770                     cn: []
34771                 }
34772             ]
34773         };
34774         
34775         if(this.href.length){
34776             cfg.href = this.href;
34777         }
34778         
34779         var cn = cfg.cn[1].cn;
34780         
34781         if(this.title.length){
34782             cn.push({
34783                 tag: 'h4',
34784                 cls: 'masonry-brick-title',
34785                 html: this.title
34786             });
34787         }
34788         
34789         if(this.html.length){
34790             cn.push({
34791                 tag: 'p',
34792                 cls: 'masonry-brick-text',
34793                 html: this.html
34794             });
34795         }
34796         
34797         if (!this.title.length && !this.html.length) {
34798             cfg.cn[1].cls += ' hide';
34799         }
34800         
34801         if(this.bgimage.length){
34802             cfg.cn.push({
34803                 tag: 'img',
34804                 cls: 'masonry-brick-image-view',
34805                 src: this.bgimage
34806             });
34807         }
34808         
34809         if(this.videourl.length){
34810             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34811             // youtube support only?
34812             cfg.cn.push({
34813                 tag: 'iframe',
34814                 cls: 'masonry-brick-image-view',
34815                 src: vurl,
34816                 frameborder : 0,
34817                 allowfullscreen : true
34818             });
34819         }
34820         
34821         return cfg;
34822         
34823     },
34824     
34825     getSplitAutoCreate : function()
34826     {
34827         var cls = 'masonry-brick masonry-brick-split';
34828         
34829         if(this.href.length){
34830             cls += ' masonry-brick-link';
34831         }
34832         
34833         if(this.bgimage.length){
34834             cls += ' masonry-brick-image';
34835         }
34836         
34837         if(this.size){
34838             cls += ' masonry-' + this.size + '-brick';
34839         }
34840         
34841         switch (this.placetitle) {
34842             case 'center' :
34843                 cls += ' masonry-center-title';
34844                 break;
34845             case 'bottom' :
34846                 cls += ' masonry-bottom-title';
34847                 break;
34848             default:
34849                 if(!this.bgimage.length){
34850                     cls += ' masonry-center-title';
34851                 }
34852
34853                 if(this.bgimage.length){
34854                     cls += ' masonry-bottom-title';
34855                 }
34856                 break;
34857         }
34858         
34859         if(this.cls){
34860             cls += ' ' + this.cls;
34861         }
34862         
34863         var cfg = {
34864             tag: (this.href.length) ? 'a' : 'div',
34865             cls: cls,
34866             cn: [
34867                 {
34868                     tag: 'div',
34869                     cls: 'masonry-brick-split-head',
34870                     cn: [
34871                         {
34872                             tag: 'div',
34873                             cls: 'masonry-brick-paragraph',
34874                             cn: []
34875                         }
34876                     ]
34877                 },
34878                 {
34879                     tag: 'div',
34880                     cls: 'masonry-brick-split-body',
34881                     cn: []
34882                 }
34883             ]
34884         };
34885         
34886         if(this.href.length){
34887             cfg.href = this.href;
34888         }
34889         
34890         if(this.title.length){
34891             cfg.cn[0].cn[0].cn.push({
34892                 tag: 'h4',
34893                 cls: 'masonry-brick-title',
34894                 html: this.title
34895             });
34896         }
34897         
34898         if(this.html.length){
34899             cfg.cn[1].cn.push({
34900                 tag: 'p',
34901                 cls: 'masonry-brick-text',
34902                 html: this.html
34903             });
34904         }
34905
34906         if(this.bgimage.length){
34907             cfg.cn[0].cn.push({
34908                 tag: 'img',
34909                 cls: 'masonry-brick-image-view',
34910                 src: this.bgimage
34911             });
34912         }
34913         
34914         if(this.videourl.length){
34915             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34916             // youtube support only?
34917             cfg.cn[0].cn.cn.push({
34918                 tag: 'iframe',
34919                 cls: 'masonry-brick-image-view',
34920                 src: vurl,
34921                 frameborder : 0,
34922                 allowfullscreen : true
34923             });
34924         }
34925         
34926         return cfg;
34927     },
34928     
34929     initEvents: function() 
34930     {
34931         switch (this.size) {
34932             case 'xs' :
34933                 this.x = 1;
34934                 this.y = 1;
34935                 break;
34936             case 'sm' :
34937                 this.x = 2;
34938                 this.y = 2;
34939                 break;
34940             case 'md' :
34941             case 'md-left' :
34942             case 'md-right' :
34943                 this.x = 3;
34944                 this.y = 3;
34945                 break;
34946             case 'tall' :
34947                 this.x = 2;
34948                 this.y = 3;
34949                 break;
34950             case 'wide' :
34951                 this.x = 3;
34952                 this.y = 2;
34953                 break;
34954             case 'wide-thin' :
34955                 this.x = 3;
34956                 this.y = 1;
34957                 break;
34958                         
34959             default :
34960                 break;
34961         }
34962         
34963         if(Roo.isTouch){
34964             this.el.on('touchstart', this.onTouchStart, this);
34965             this.el.on('touchmove', this.onTouchMove, this);
34966             this.el.on('touchend', this.onTouchEnd, this);
34967             this.el.on('contextmenu', this.onContextMenu, this);
34968         } else {
34969             this.el.on('mouseenter'  ,this.enter, this);
34970             this.el.on('mouseleave', this.leave, this);
34971             this.el.on('click', this.onClick, this);
34972         }
34973         
34974         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34975             this.parent().bricks.push(this);   
34976         }
34977         
34978     },
34979     
34980     onClick: function(e, el)
34981     {
34982         var time = this.endTimer - this.startTimer;
34983         // Roo.log(e.preventDefault());
34984         if(Roo.isTouch){
34985             if(time > 1000){
34986                 e.preventDefault();
34987                 return;
34988             }
34989         }
34990         
34991         if(!this.preventDefault){
34992             return;
34993         }
34994         
34995         e.preventDefault();
34996         
34997         if (this.activeClass != '') {
34998             this.selectBrick();
34999         }
35000         
35001         this.fireEvent('click', this, e);
35002     },
35003     
35004     enter: function(e, el)
35005     {
35006         e.preventDefault();
35007         
35008         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35009             return;
35010         }
35011         
35012         if(this.bgimage.length && this.html.length){
35013             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35014         }
35015     },
35016     
35017     leave: function(e, el)
35018     {
35019         e.preventDefault();
35020         
35021         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35022             return;
35023         }
35024         
35025         if(this.bgimage.length && this.html.length){
35026             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35027         }
35028     },
35029     
35030     onTouchStart: function(e, el)
35031     {
35032 //        e.preventDefault();
35033         
35034         this.touchmoved = false;
35035         
35036         if(!this.isFitContainer){
35037             return;
35038         }
35039         
35040         if(!this.bgimage.length || !this.html.length){
35041             return;
35042         }
35043         
35044         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35045         
35046         this.timer = new Date().getTime();
35047         
35048     },
35049     
35050     onTouchMove: function(e, el)
35051     {
35052         this.touchmoved = true;
35053     },
35054     
35055     onContextMenu : function(e,el)
35056     {
35057         e.preventDefault();
35058         e.stopPropagation();
35059         return false;
35060     },
35061     
35062     onTouchEnd: function(e, el)
35063     {
35064 //        e.preventDefault();
35065         
35066         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35067         
35068             this.leave(e,el);
35069             
35070             return;
35071         }
35072         
35073         if(!this.bgimage.length || !this.html.length){
35074             
35075             if(this.href.length){
35076                 window.location.href = this.href;
35077             }
35078             
35079             return;
35080         }
35081         
35082         if(!this.isFitContainer){
35083             return;
35084         }
35085         
35086         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35087         
35088         window.location.href = this.href;
35089     },
35090     
35091     //selection on single brick only
35092     selectBrick : function() {
35093         
35094         if (!this.parentId) {
35095             return;
35096         }
35097         
35098         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35099         var index = m.selectedBrick.indexOf(this.id);
35100         
35101         if ( index > -1) {
35102             m.selectedBrick.splice(index,1);
35103             this.el.removeClass(this.activeClass);
35104             return;
35105         }
35106         
35107         for(var i = 0; i < m.selectedBrick.length; i++) {
35108             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35109             b.el.removeClass(b.activeClass);
35110         }
35111         
35112         m.selectedBrick = [];
35113         
35114         m.selectedBrick.push(this.id);
35115         this.el.addClass(this.activeClass);
35116         return;
35117     },
35118     
35119     isSelected : function(){
35120         return this.el.hasClass(this.activeClass);
35121         
35122     }
35123 });
35124
35125 Roo.apply(Roo.bootstrap.MasonryBrick, {
35126     
35127     //groups: {},
35128     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35129      /**
35130     * register a Masonry Brick
35131     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35132     */
35133     
35134     register : function(brick)
35135     {
35136         //this.groups[brick.id] = brick;
35137         this.groups.add(brick.id, brick);
35138     },
35139     /**
35140     * fetch a  masonry brick based on the masonry brick ID
35141     * @param {string} the masonry brick to add
35142     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35143     */
35144     
35145     get: function(brick_id) 
35146     {
35147         // if (typeof(this.groups[brick_id]) == 'undefined') {
35148         //     return false;
35149         // }
35150         // return this.groups[brick_id] ;
35151         
35152         if(this.groups.key(brick_id)) {
35153             return this.groups.key(brick_id);
35154         }
35155         
35156         return false;
35157     }
35158     
35159     
35160     
35161 });
35162
35163  /*
35164  * - LGPL
35165  *
35166  * element
35167  * 
35168  */
35169
35170 /**
35171  * @class Roo.bootstrap.Brick
35172  * @extends Roo.bootstrap.Component
35173  * Bootstrap Brick class
35174  * 
35175  * @constructor
35176  * Create a new Brick
35177  * @param {Object} config The config object
35178  */
35179
35180 Roo.bootstrap.Brick = function(config){
35181     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35182     
35183     this.addEvents({
35184         // raw events
35185         /**
35186          * @event click
35187          * When a Brick is click
35188          * @param {Roo.bootstrap.Brick} this
35189          * @param {Roo.EventObject} e
35190          */
35191         "click" : true
35192     });
35193 };
35194
35195 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35196     
35197     /**
35198      * @cfg {String} title
35199      */   
35200     title : '',
35201     /**
35202      * @cfg {String} html
35203      */   
35204     html : '',
35205     /**
35206      * @cfg {String} bgimage
35207      */   
35208     bgimage : '',
35209     /**
35210      * @cfg {String} cls
35211      */   
35212     cls : '',
35213     /**
35214      * @cfg {String} href
35215      */   
35216     href : '',
35217     /**
35218      * @cfg {String} video
35219      */   
35220     video : '',
35221     /**
35222      * @cfg {Boolean} square
35223      */   
35224     square : true,
35225     
35226     getAutoCreate : function()
35227     {
35228         var cls = 'roo-brick';
35229         
35230         if(this.href.length){
35231             cls += ' roo-brick-link';
35232         }
35233         
35234         if(this.bgimage.length){
35235             cls += ' roo-brick-image';
35236         }
35237         
35238         if(!this.html.length && !this.bgimage.length){
35239             cls += ' roo-brick-center-title';
35240         }
35241         
35242         if(!this.html.length && this.bgimage.length){
35243             cls += ' roo-brick-bottom-title';
35244         }
35245         
35246         if(this.cls){
35247             cls += ' ' + this.cls;
35248         }
35249         
35250         var cfg = {
35251             tag: (this.href.length) ? 'a' : 'div',
35252             cls: cls,
35253             cn: [
35254                 {
35255                     tag: 'div',
35256                     cls: 'roo-brick-paragraph',
35257                     cn: []
35258                 }
35259             ]
35260         };
35261         
35262         if(this.href.length){
35263             cfg.href = this.href;
35264         }
35265         
35266         var cn = cfg.cn[0].cn;
35267         
35268         if(this.title.length){
35269             cn.push({
35270                 tag: 'h4',
35271                 cls: 'roo-brick-title',
35272                 html: this.title
35273             });
35274         }
35275         
35276         if(this.html.length){
35277             cn.push({
35278                 tag: 'p',
35279                 cls: 'roo-brick-text',
35280                 html: this.html
35281             });
35282         } else {
35283             cn.cls += ' hide';
35284         }
35285         
35286         if(this.bgimage.length){
35287             cfg.cn.push({
35288                 tag: 'img',
35289                 cls: 'roo-brick-image-view',
35290                 src: this.bgimage
35291             });
35292         }
35293         
35294         return cfg;
35295     },
35296     
35297     initEvents: function() 
35298     {
35299         if(this.title.length || this.html.length){
35300             this.el.on('mouseenter'  ,this.enter, this);
35301             this.el.on('mouseleave', this.leave, this);
35302         }
35303         
35304         Roo.EventManager.onWindowResize(this.resize, this); 
35305         
35306         if(this.bgimage.length){
35307             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35308             this.imageEl.on('load', this.onImageLoad, this);
35309             return;
35310         }
35311         
35312         this.resize();
35313     },
35314     
35315     onImageLoad : function()
35316     {
35317         this.resize();
35318     },
35319     
35320     resize : function()
35321     {
35322         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35323         
35324         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35325         
35326         if(this.bgimage.length){
35327             var image = this.el.select('.roo-brick-image-view', true).first();
35328             
35329             image.setWidth(paragraph.getWidth());
35330             
35331             if(this.square){
35332                 image.setHeight(paragraph.getWidth());
35333             }
35334             
35335             this.el.setHeight(image.getHeight());
35336             paragraph.setHeight(image.getHeight());
35337             
35338         }
35339         
35340     },
35341     
35342     enter: function(e, el)
35343     {
35344         e.preventDefault();
35345         
35346         if(this.bgimage.length){
35347             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35348             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35349         }
35350     },
35351     
35352     leave: function(e, el)
35353     {
35354         e.preventDefault();
35355         
35356         if(this.bgimage.length){
35357             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35358             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35359         }
35360     }
35361     
35362 });
35363
35364  
35365
35366  /*
35367  * - LGPL
35368  *
35369  * Number field 
35370  */
35371
35372 /**
35373  * @class Roo.bootstrap.NumberField
35374  * @extends Roo.bootstrap.Input
35375  * Bootstrap NumberField class
35376  * 
35377  * 
35378  * 
35379  * 
35380  * @constructor
35381  * Create a new NumberField
35382  * @param {Object} config The config object
35383  */
35384
35385 Roo.bootstrap.NumberField = function(config){
35386     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35387 };
35388
35389 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35390     
35391     /**
35392      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35393      */
35394     allowDecimals : true,
35395     /**
35396      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35397      */
35398     decimalSeparator : ".",
35399     /**
35400      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35401      */
35402     decimalPrecision : 2,
35403     /**
35404      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35405      */
35406     allowNegative : true,
35407     
35408     /**
35409      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35410      */
35411     allowZero: true,
35412     /**
35413      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35414      */
35415     minValue : Number.NEGATIVE_INFINITY,
35416     /**
35417      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35418      */
35419     maxValue : Number.MAX_VALUE,
35420     /**
35421      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35422      */
35423     minText : "The minimum value for this field is {0}",
35424     /**
35425      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35426      */
35427     maxText : "The maximum value for this field is {0}",
35428     /**
35429      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35430      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35431      */
35432     nanText : "{0} is not a valid number",
35433     /**
35434      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35435      */
35436     thousandsDelimiter : false,
35437     /**
35438      * @cfg {String} valueAlign alignment of value
35439      */
35440     valueAlign : "left",
35441
35442     getAutoCreate : function()
35443     {
35444         var hiddenInput = {
35445             tag: 'input',
35446             type: 'hidden',
35447             id: Roo.id(),
35448             cls: 'hidden-number-input'
35449         };
35450         
35451         if (this.name) {
35452             hiddenInput.name = this.name;
35453         }
35454         
35455         this.name = '';
35456         
35457         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35458         
35459         this.name = hiddenInput.name;
35460         
35461         if(cfg.cn.length > 0) {
35462             cfg.cn.push(hiddenInput);
35463         }
35464         
35465         return cfg;
35466     },
35467
35468     // private
35469     initEvents : function()
35470     {   
35471         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35472         
35473         var allowed = "0123456789";
35474         
35475         if(this.allowDecimals){
35476             allowed += this.decimalSeparator;
35477         }
35478         
35479         if(this.allowNegative){
35480             allowed += "-";
35481         }
35482         
35483         if(this.thousandsDelimiter) {
35484             allowed += ",";
35485         }
35486         
35487         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35488         
35489         var keyPress = function(e){
35490             
35491             var k = e.getKey();
35492             
35493             var c = e.getCharCode();
35494             
35495             if(
35496                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35497                     allowed.indexOf(String.fromCharCode(c)) === -1
35498             ){
35499                 e.stopEvent();
35500                 return;
35501             }
35502             
35503             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35504                 return;
35505             }
35506             
35507             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35508                 e.stopEvent();
35509             }
35510         };
35511         
35512         this.el.on("keypress", keyPress, this);
35513     },
35514     
35515     validateValue : function(value)
35516     {
35517         
35518         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35519             return false;
35520         }
35521         
35522         var num = this.parseValue(value);
35523         
35524         if(isNaN(num)){
35525             this.markInvalid(String.format(this.nanText, value));
35526             return false;
35527         }
35528         
35529         if(num < this.minValue){
35530             this.markInvalid(String.format(this.minText, this.minValue));
35531             return false;
35532         }
35533         
35534         if(num > this.maxValue){
35535             this.markInvalid(String.format(this.maxText, this.maxValue));
35536             return false;
35537         }
35538         
35539         return true;
35540     },
35541
35542     getValue : function()
35543     {
35544         var v = this.hiddenEl().getValue();
35545         
35546         return this.fixPrecision(this.parseValue(v));
35547     },
35548
35549     parseValue : function(value)
35550     {
35551         if(this.thousandsDelimiter) {
35552             value += "";
35553             r = new RegExp(",", "g");
35554             value = value.replace(r, "");
35555         }
35556         
35557         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35558         return isNaN(value) ? '' : value;
35559     },
35560
35561     fixPrecision : function(value)
35562     {
35563         if(this.thousandsDelimiter) {
35564             value += "";
35565             r = new RegExp(",", "g");
35566             value = value.replace(r, "");
35567         }
35568         
35569         var nan = isNaN(value);
35570         
35571         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35572             return nan ? '' : value;
35573         }
35574         return parseFloat(value).toFixed(this.decimalPrecision);
35575     },
35576
35577     setValue : function(v)
35578     {
35579         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35580         
35581         this.value = v;
35582         
35583         if(this.rendered){
35584             
35585             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35586             
35587             this.inputEl().dom.value = (v == '') ? '' :
35588                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35589             
35590             if(!this.allowZero && v === '0') {
35591                 this.hiddenEl().dom.value = '';
35592                 this.inputEl().dom.value = '';
35593             }
35594             
35595             this.validate();
35596         }
35597     },
35598
35599     decimalPrecisionFcn : function(v)
35600     {
35601         return Math.floor(v);
35602     },
35603
35604     beforeBlur : function()
35605     {
35606         var v = this.parseValue(this.getRawValue());
35607         
35608         if(v || v === 0 || v === ''){
35609             this.setValue(v);
35610         }
35611     },
35612     
35613     hiddenEl : function()
35614     {
35615         return this.el.select('input.hidden-number-input',true).first();
35616     }
35617     
35618 });
35619
35620  
35621
35622 /*
35623 * Licence: LGPL
35624 */
35625
35626 /**
35627  * @class Roo.bootstrap.DocumentSlider
35628  * @extends Roo.bootstrap.Component
35629  * Bootstrap DocumentSlider class
35630  * 
35631  * @constructor
35632  * Create a new DocumentViewer
35633  * @param {Object} config The config object
35634  */
35635
35636 Roo.bootstrap.DocumentSlider = function(config){
35637     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35638     
35639     this.files = [];
35640     
35641     this.addEvents({
35642         /**
35643          * @event initial
35644          * Fire after initEvent
35645          * @param {Roo.bootstrap.DocumentSlider} this
35646          */
35647         "initial" : true,
35648         /**
35649          * @event update
35650          * Fire after update
35651          * @param {Roo.bootstrap.DocumentSlider} this
35652          */
35653         "update" : true,
35654         /**
35655          * @event click
35656          * Fire after click
35657          * @param {Roo.bootstrap.DocumentSlider} this
35658          */
35659         "click" : true
35660     });
35661 };
35662
35663 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35664     
35665     files : false,
35666     
35667     indicator : 0,
35668     
35669     getAutoCreate : function()
35670     {
35671         var cfg = {
35672             tag : 'div',
35673             cls : 'roo-document-slider',
35674             cn : [
35675                 {
35676                     tag : 'div',
35677                     cls : 'roo-document-slider-header',
35678                     cn : [
35679                         {
35680                             tag : 'div',
35681                             cls : 'roo-document-slider-header-title'
35682                         }
35683                     ]
35684                 },
35685                 {
35686                     tag : 'div',
35687                     cls : 'roo-document-slider-body',
35688                     cn : [
35689                         {
35690                             tag : 'div',
35691                             cls : 'roo-document-slider-prev',
35692                             cn : [
35693                                 {
35694                                     tag : 'i',
35695                                     cls : 'fa fa-chevron-left'
35696                                 }
35697                             ]
35698                         },
35699                         {
35700                             tag : 'div',
35701                             cls : 'roo-document-slider-thumb',
35702                             cn : [
35703                                 {
35704                                     tag : 'img',
35705                                     cls : 'roo-document-slider-image'
35706                                 }
35707                             ]
35708                         },
35709                         {
35710                             tag : 'div',
35711                             cls : 'roo-document-slider-next',
35712                             cn : [
35713                                 {
35714                                     tag : 'i',
35715                                     cls : 'fa fa-chevron-right'
35716                                 }
35717                             ]
35718                         }
35719                     ]
35720                 }
35721             ]
35722         };
35723         
35724         return cfg;
35725     },
35726     
35727     initEvents : function()
35728     {
35729         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35730         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35731         
35732         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35733         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35734         
35735         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35736         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35737         
35738         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35739         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35740         
35741         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35742         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35743         
35744         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35745         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35746         
35747         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35748         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35749         
35750         this.thumbEl.on('click', this.onClick, this);
35751         
35752         this.prevIndicator.on('click', this.prev, this);
35753         
35754         this.nextIndicator.on('click', this.next, this);
35755         
35756     },
35757     
35758     initial : function()
35759     {
35760         if(this.files.length){
35761             this.indicator = 1;
35762             this.update()
35763         }
35764         
35765         this.fireEvent('initial', this);
35766     },
35767     
35768     update : function()
35769     {
35770         this.imageEl.attr('src', this.files[this.indicator - 1]);
35771         
35772         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35773         
35774         this.prevIndicator.show();
35775         
35776         if(this.indicator == 1){
35777             this.prevIndicator.hide();
35778         }
35779         
35780         this.nextIndicator.show();
35781         
35782         if(this.indicator == this.files.length){
35783             this.nextIndicator.hide();
35784         }
35785         
35786         this.thumbEl.scrollTo('top');
35787         
35788         this.fireEvent('update', this);
35789     },
35790     
35791     onClick : function(e)
35792     {
35793         e.preventDefault();
35794         
35795         this.fireEvent('click', this);
35796     },
35797     
35798     prev : function(e)
35799     {
35800         e.preventDefault();
35801         
35802         this.indicator = Math.max(1, this.indicator - 1);
35803         
35804         this.update();
35805     },
35806     
35807     next : function(e)
35808     {
35809         e.preventDefault();
35810         
35811         this.indicator = Math.min(this.files.length, this.indicator + 1);
35812         
35813         this.update();
35814     }
35815 });
35816 /*
35817  * - LGPL
35818  *
35819  * RadioSet
35820  *
35821  *
35822  */
35823
35824 /**
35825  * @class Roo.bootstrap.RadioSet
35826  * @extends Roo.bootstrap.Input
35827  * Bootstrap RadioSet class
35828  * @cfg {String} indicatorpos (left|right) default left
35829  * @cfg {Boolean} inline (true|false) inline the element (default true)
35830  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35831  * @constructor
35832  * Create a new RadioSet
35833  * @param {Object} config The config object
35834  */
35835
35836 Roo.bootstrap.RadioSet = function(config){
35837     
35838     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35839     
35840     this.radioes = [];
35841     
35842     Roo.bootstrap.RadioSet.register(this);
35843     
35844     this.addEvents({
35845         /**
35846         * @event check
35847         * Fires when the element is checked or unchecked.
35848         * @param {Roo.bootstrap.RadioSet} this This radio
35849         * @param {Roo.bootstrap.Radio} item The checked item
35850         */
35851        check : true,
35852        /**
35853         * @event click
35854         * Fires when the element is click.
35855         * @param {Roo.bootstrap.RadioSet} this This radio set
35856         * @param {Roo.bootstrap.Radio} item The checked item
35857         * @param {Roo.EventObject} e The event object
35858         */
35859        click : true
35860     });
35861     
35862 };
35863
35864 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35865
35866     radioes : false,
35867     
35868     inline : true,
35869     
35870     weight : '',
35871     
35872     indicatorpos : 'left',
35873     
35874     getAutoCreate : function()
35875     {
35876         var label = {
35877             tag : 'label',
35878             cls : 'roo-radio-set-label',
35879             cn : [
35880                 {
35881                     tag : 'span',
35882                     html : this.fieldLabel
35883                 }
35884             ]
35885         };
35886         if (Roo.bootstrap.version == 3) {
35887             
35888             
35889             if(this.indicatorpos == 'left'){
35890                 label.cn.unshift({
35891                     tag : 'i',
35892                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35893                     tooltip : 'This field is required'
35894                 });
35895             } else {
35896                 label.cn.push({
35897                     tag : 'i',
35898                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35899                     tooltip : 'This field is required'
35900                 });
35901             }
35902         }
35903         var items = {
35904             tag : 'div',
35905             cls : 'roo-radio-set-items'
35906         };
35907         
35908         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35909         
35910         if (align === 'left' && this.fieldLabel.length) {
35911             
35912             items = {
35913                 cls : "roo-radio-set-right", 
35914                 cn: [
35915                     items
35916                 ]
35917             };
35918             
35919             if(this.labelWidth > 12){
35920                 label.style = "width: " + this.labelWidth + 'px';
35921             }
35922             
35923             if(this.labelWidth < 13 && this.labelmd == 0){
35924                 this.labelmd = this.labelWidth;
35925             }
35926             
35927             if(this.labellg > 0){
35928                 label.cls += ' col-lg-' + this.labellg;
35929                 items.cls += ' col-lg-' + (12 - this.labellg);
35930             }
35931             
35932             if(this.labelmd > 0){
35933                 label.cls += ' col-md-' + this.labelmd;
35934                 items.cls += ' col-md-' + (12 - this.labelmd);
35935             }
35936             
35937             if(this.labelsm > 0){
35938                 label.cls += ' col-sm-' + this.labelsm;
35939                 items.cls += ' col-sm-' + (12 - this.labelsm);
35940             }
35941             
35942             if(this.labelxs > 0){
35943                 label.cls += ' col-xs-' + this.labelxs;
35944                 items.cls += ' col-xs-' + (12 - this.labelxs);
35945             }
35946         }
35947         
35948         var cfg = {
35949             tag : 'div',
35950             cls : 'roo-radio-set',
35951             cn : [
35952                 {
35953                     tag : 'input',
35954                     cls : 'roo-radio-set-input',
35955                     type : 'hidden',
35956                     name : this.name,
35957                     value : this.value ? this.value :  ''
35958                 },
35959                 label,
35960                 items
35961             ]
35962         };
35963         
35964         if(this.weight.length){
35965             cfg.cls += ' roo-radio-' + this.weight;
35966         }
35967         
35968         if(this.inline) {
35969             cfg.cls += ' roo-radio-set-inline';
35970         }
35971         
35972         var settings=this;
35973         ['xs','sm','md','lg'].map(function(size){
35974             if (settings[size]) {
35975                 cfg.cls += ' col-' + size + '-' + settings[size];
35976             }
35977         });
35978         
35979         return cfg;
35980         
35981     },
35982
35983     initEvents : function()
35984     {
35985         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
35986         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
35987         
35988         if(!this.fieldLabel.length){
35989             this.labelEl.hide();
35990         }
35991         
35992         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
35993         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
35994         
35995         this.indicator = this.indicatorEl();
35996         
35997         if(this.indicator){
35998             this.indicator.addClass('invisible');
35999         }
36000         
36001         this.originalValue = this.getValue();
36002         
36003     },
36004     
36005     inputEl: function ()
36006     {
36007         return this.el.select('.roo-radio-set-input', true).first();
36008     },
36009     
36010     getChildContainer : function()
36011     {
36012         return this.itemsEl;
36013     },
36014     
36015     register : function(item)
36016     {
36017         this.radioes.push(item);
36018         
36019     },
36020     
36021     validate : function()
36022     {   
36023         if(this.getVisibilityEl().hasClass('hidden')){
36024             return true;
36025         }
36026         
36027         var valid = false;
36028         
36029         Roo.each(this.radioes, function(i){
36030             if(!i.checked){
36031                 return;
36032             }
36033             
36034             valid = true;
36035             return false;
36036         });
36037         
36038         if(this.allowBlank) {
36039             return true;
36040         }
36041         
36042         if(this.disabled || valid){
36043             this.markValid();
36044             return true;
36045         }
36046         
36047         this.markInvalid();
36048         return false;
36049         
36050     },
36051     
36052     markValid : function()
36053     {
36054         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36055             this.indicatorEl().removeClass('visible');
36056             this.indicatorEl().addClass('invisible');
36057         }
36058         
36059         
36060         if (Roo.bootstrap.version == 3) {
36061             this.el.removeClass([this.invalidClass, this.validClass]);
36062             this.el.addClass(this.validClass);
36063         } else {
36064             this.el.removeClass(['is-invalid','is-valid']);
36065             this.el.addClass(['is-valid']);
36066         }
36067         this.fireEvent('valid', this);
36068     },
36069     
36070     markInvalid : function(msg)
36071     {
36072         if(this.allowBlank || this.disabled){
36073             return;
36074         }
36075         
36076         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36077             this.indicatorEl().removeClass('invisible');
36078             this.indicatorEl().addClass('visible');
36079         }
36080         if (Roo.bootstrap.version == 3) {
36081             this.el.removeClass([this.invalidClass, this.validClass]);
36082             this.el.addClass(this.invalidClass);
36083         } else {
36084             this.el.removeClass(['is-invalid','is-valid']);
36085             this.el.addClass(['is-invalid']);
36086         }
36087         
36088         this.fireEvent('invalid', this, msg);
36089         
36090     },
36091     
36092     setValue : function(v, suppressEvent)
36093     {   
36094         if(this.value === v){
36095             return;
36096         }
36097         
36098         this.value = v;
36099         
36100         if(this.rendered){
36101             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36102         }
36103         
36104         Roo.each(this.radioes, function(i){
36105             i.checked = false;
36106             i.el.removeClass('checked');
36107         });
36108         
36109         Roo.each(this.radioes, function(i){
36110             
36111             if(i.value === v || i.value.toString() === v.toString()){
36112                 i.checked = true;
36113                 i.el.addClass('checked');
36114                 
36115                 if(suppressEvent !== true){
36116                     this.fireEvent('check', this, i);
36117                 }
36118                 
36119                 return false;
36120             }
36121             
36122         }, this);
36123         
36124         this.validate();
36125     },
36126     
36127     clearInvalid : function(){
36128         
36129         if(!this.el || this.preventMark){
36130             return;
36131         }
36132         
36133         this.el.removeClass([this.invalidClass]);
36134         
36135         this.fireEvent('valid', this);
36136     }
36137     
36138 });
36139
36140 Roo.apply(Roo.bootstrap.RadioSet, {
36141     
36142     groups: {},
36143     
36144     register : function(set)
36145     {
36146         this.groups[set.name] = set;
36147     },
36148     
36149     get: function(name) 
36150     {
36151         if (typeof(this.groups[name]) == 'undefined') {
36152             return false;
36153         }
36154         
36155         return this.groups[name] ;
36156     }
36157     
36158 });
36159 /*
36160  * Based on:
36161  * Ext JS Library 1.1.1
36162  * Copyright(c) 2006-2007, Ext JS, LLC.
36163  *
36164  * Originally Released Under LGPL - original licence link has changed is not relivant.
36165  *
36166  * Fork - LGPL
36167  * <script type="text/javascript">
36168  */
36169
36170
36171 /**
36172  * @class Roo.bootstrap.SplitBar
36173  * @extends Roo.util.Observable
36174  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36175  * <br><br>
36176  * Usage:
36177  * <pre><code>
36178 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36179                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36180 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36181 split.minSize = 100;
36182 split.maxSize = 600;
36183 split.animate = true;
36184 split.on('moved', splitterMoved);
36185 </code></pre>
36186  * @constructor
36187  * Create a new SplitBar
36188  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36189  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36190  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36191  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36192                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36193                         position of the SplitBar).
36194  */
36195 Roo.bootstrap.SplitBar = function(cfg){
36196     
36197     /** @private */
36198     
36199     //{
36200     //  dragElement : elm
36201     //  resizingElement: el,
36202         // optional..
36203     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36204     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36205         // existingProxy ???
36206     //}
36207     
36208     this.el = Roo.get(cfg.dragElement, true);
36209     this.el.dom.unselectable = "on";
36210     /** @private */
36211     this.resizingEl = Roo.get(cfg.resizingElement, true);
36212
36213     /**
36214      * @private
36215      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36216      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36217      * @type Number
36218      */
36219     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36220     
36221     /**
36222      * The minimum size of the resizing element. (Defaults to 0)
36223      * @type Number
36224      */
36225     this.minSize = 0;
36226     
36227     /**
36228      * The maximum size of the resizing element. (Defaults to 2000)
36229      * @type Number
36230      */
36231     this.maxSize = 2000;
36232     
36233     /**
36234      * Whether to animate the transition to the new size
36235      * @type Boolean
36236      */
36237     this.animate = false;
36238     
36239     /**
36240      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36241      * @type Boolean
36242      */
36243     this.useShim = false;
36244     
36245     /** @private */
36246     this.shim = null;
36247     
36248     if(!cfg.existingProxy){
36249         /** @private */
36250         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36251     }else{
36252         this.proxy = Roo.get(cfg.existingProxy).dom;
36253     }
36254     /** @private */
36255     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36256     
36257     /** @private */
36258     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36259     
36260     /** @private */
36261     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36262     
36263     /** @private */
36264     this.dragSpecs = {};
36265     
36266     /**
36267      * @private The adapter to use to positon and resize elements
36268      */
36269     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36270     this.adapter.init(this);
36271     
36272     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36273         /** @private */
36274         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36275         this.el.addClass("roo-splitbar-h");
36276     }else{
36277         /** @private */
36278         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36279         this.el.addClass("roo-splitbar-v");
36280     }
36281     
36282     this.addEvents({
36283         /**
36284          * @event resize
36285          * Fires when the splitter is moved (alias for {@link #event-moved})
36286          * @param {Roo.bootstrap.SplitBar} this
36287          * @param {Number} newSize the new width or height
36288          */
36289         "resize" : true,
36290         /**
36291          * @event moved
36292          * Fires when the splitter is moved
36293          * @param {Roo.bootstrap.SplitBar} this
36294          * @param {Number} newSize the new width or height
36295          */
36296         "moved" : true,
36297         /**
36298          * @event beforeresize
36299          * Fires before the splitter is dragged
36300          * @param {Roo.bootstrap.SplitBar} this
36301          */
36302         "beforeresize" : true,
36303
36304         "beforeapply" : true
36305     });
36306
36307     Roo.util.Observable.call(this);
36308 };
36309
36310 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36311     onStartProxyDrag : function(x, y){
36312         this.fireEvent("beforeresize", this);
36313         if(!this.overlay){
36314             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36315             o.unselectable();
36316             o.enableDisplayMode("block");
36317             // all splitbars share the same overlay
36318             Roo.bootstrap.SplitBar.prototype.overlay = o;
36319         }
36320         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36321         this.overlay.show();
36322         Roo.get(this.proxy).setDisplayed("block");
36323         var size = this.adapter.getElementSize(this);
36324         this.activeMinSize = this.getMinimumSize();;
36325         this.activeMaxSize = this.getMaximumSize();;
36326         var c1 = size - this.activeMinSize;
36327         var c2 = Math.max(this.activeMaxSize - size, 0);
36328         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36329             this.dd.resetConstraints();
36330             this.dd.setXConstraint(
36331                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36332                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36333             );
36334             this.dd.setYConstraint(0, 0);
36335         }else{
36336             this.dd.resetConstraints();
36337             this.dd.setXConstraint(0, 0);
36338             this.dd.setYConstraint(
36339                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36340                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36341             );
36342          }
36343         this.dragSpecs.startSize = size;
36344         this.dragSpecs.startPoint = [x, y];
36345         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36346     },
36347     
36348     /** 
36349      * @private Called after the drag operation by the DDProxy
36350      */
36351     onEndProxyDrag : function(e){
36352         Roo.get(this.proxy).setDisplayed(false);
36353         var endPoint = Roo.lib.Event.getXY(e);
36354         if(this.overlay){
36355             this.overlay.hide();
36356         }
36357         var newSize;
36358         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36359             newSize = this.dragSpecs.startSize + 
36360                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36361                     endPoint[0] - this.dragSpecs.startPoint[0] :
36362                     this.dragSpecs.startPoint[0] - endPoint[0]
36363                 );
36364         }else{
36365             newSize = this.dragSpecs.startSize + 
36366                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36367                     endPoint[1] - this.dragSpecs.startPoint[1] :
36368                     this.dragSpecs.startPoint[1] - endPoint[1]
36369                 );
36370         }
36371         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36372         if(newSize != this.dragSpecs.startSize){
36373             if(this.fireEvent('beforeapply', this, newSize) !== false){
36374                 this.adapter.setElementSize(this, newSize);
36375                 this.fireEvent("moved", this, newSize);
36376                 this.fireEvent("resize", this, newSize);
36377             }
36378         }
36379     },
36380     
36381     /**
36382      * Get the adapter this SplitBar uses
36383      * @return The adapter object
36384      */
36385     getAdapter : function(){
36386         return this.adapter;
36387     },
36388     
36389     /**
36390      * Set the adapter this SplitBar uses
36391      * @param {Object} adapter A SplitBar adapter object
36392      */
36393     setAdapter : function(adapter){
36394         this.adapter = adapter;
36395         this.adapter.init(this);
36396     },
36397     
36398     /**
36399      * Gets the minimum size for the resizing element
36400      * @return {Number} The minimum size
36401      */
36402     getMinimumSize : function(){
36403         return this.minSize;
36404     },
36405     
36406     /**
36407      * Sets the minimum size for the resizing element
36408      * @param {Number} minSize The minimum size
36409      */
36410     setMinimumSize : function(minSize){
36411         this.minSize = minSize;
36412     },
36413     
36414     /**
36415      * Gets the maximum size for the resizing element
36416      * @return {Number} The maximum size
36417      */
36418     getMaximumSize : function(){
36419         return this.maxSize;
36420     },
36421     
36422     /**
36423      * Sets the maximum size for the resizing element
36424      * @param {Number} maxSize The maximum size
36425      */
36426     setMaximumSize : function(maxSize){
36427         this.maxSize = maxSize;
36428     },
36429     
36430     /**
36431      * Sets the initialize size for the resizing element
36432      * @param {Number} size The initial size
36433      */
36434     setCurrentSize : function(size){
36435         var oldAnimate = this.animate;
36436         this.animate = false;
36437         this.adapter.setElementSize(this, size);
36438         this.animate = oldAnimate;
36439     },
36440     
36441     /**
36442      * Destroy this splitbar. 
36443      * @param {Boolean} removeEl True to remove the element
36444      */
36445     destroy : function(removeEl){
36446         if(this.shim){
36447             this.shim.remove();
36448         }
36449         this.dd.unreg();
36450         this.proxy.parentNode.removeChild(this.proxy);
36451         if(removeEl){
36452             this.el.remove();
36453         }
36454     }
36455 });
36456
36457 /**
36458  * @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.
36459  */
36460 Roo.bootstrap.SplitBar.createProxy = function(dir){
36461     var proxy = new Roo.Element(document.createElement("div"));
36462     proxy.unselectable();
36463     var cls = 'roo-splitbar-proxy';
36464     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36465     document.body.appendChild(proxy.dom);
36466     return proxy.dom;
36467 };
36468
36469 /** 
36470  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36471  * Default Adapter. It assumes the splitter and resizing element are not positioned
36472  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36473  */
36474 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36475 };
36476
36477 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36478     // do nothing for now
36479     init : function(s){
36480     
36481     },
36482     /**
36483      * Called before drag operations to get the current size of the resizing element. 
36484      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36485      */
36486      getElementSize : function(s){
36487         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36488             return s.resizingEl.getWidth();
36489         }else{
36490             return s.resizingEl.getHeight();
36491         }
36492     },
36493     
36494     /**
36495      * Called after drag operations to set the size of the resizing element.
36496      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36497      * @param {Number} newSize The new size to set
36498      * @param {Function} onComplete A function to be invoked when resizing is complete
36499      */
36500     setElementSize : function(s, newSize, onComplete){
36501         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36502             if(!s.animate){
36503                 s.resizingEl.setWidth(newSize);
36504                 if(onComplete){
36505                     onComplete(s, newSize);
36506                 }
36507             }else{
36508                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36509             }
36510         }else{
36511             
36512             if(!s.animate){
36513                 s.resizingEl.setHeight(newSize);
36514                 if(onComplete){
36515                     onComplete(s, newSize);
36516                 }
36517             }else{
36518                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36519             }
36520         }
36521     }
36522 };
36523
36524 /** 
36525  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36526  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36527  * Adapter that  moves the splitter element to align with the resized sizing element. 
36528  * Used with an absolute positioned SplitBar.
36529  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36530  * document.body, make sure you assign an id to the body element.
36531  */
36532 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36533     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36534     this.container = Roo.get(container);
36535 };
36536
36537 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36538     init : function(s){
36539         this.basic.init(s);
36540     },
36541     
36542     getElementSize : function(s){
36543         return this.basic.getElementSize(s);
36544     },
36545     
36546     setElementSize : function(s, newSize, onComplete){
36547         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36548     },
36549     
36550     moveSplitter : function(s){
36551         var yes = Roo.bootstrap.SplitBar;
36552         switch(s.placement){
36553             case yes.LEFT:
36554                 s.el.setX(s.resizingEl.getRight());
36555                 break;
36556             case yes.RIGHT:
36557                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36558                 break;
36559             case yes.TOP:
36560                 s.el.setY(s.resizingEl.getBottom());
36561                 break;
36562             case yes.BOTTOM:
36563                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36564                 break;
36565         }
36566     }
36567 };
36568
36569 /**
36570  * Orientation constant - Create a vertical SplitBar
36571  * @static
36572  * @type Number
36573  */
36574 Roo.bootstrap.SplitBar.VERTICAL = 1;
36575
36576 /**
36577  * Orientation constant - Create a horizontal SplitBar
36578  * @static
36579  * @type Number
36580  */
36581 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36582
36583 /**
36584  * Placement constant - The resizing element is to the left of the splitter element
36585  * @static
36586  * @type Number
36587  */
36588 Roo.bootstrap.SplitBar.LEFT = 1;
36589
36590 /**
36591  * Placement constant - The resizing element is to the right of the splitter element
36592  * @static
36593  * @type Number
36594  */
36595 Roo.bootstrap.SplitBar.RIGHT = 2;
36596
36597 /**
36598  * Placement constant - The resizing element is positioned above the splitter element
36599  * @static
36600  * @type Number
36601  */
36602 Roo.bootstrap.SplitBar.TOP = 3;
36603
36604 /**
36605  * Placement constant - The resizing element is positioned under splitter element
36606  * @static
36607  * @type Number
36608  */
36609 Roo.bootstrap.SplitBar.BOTTOM = 4;
36610 Roo.namespace("Roo.bootstrap.layout");/*
36611  * Based on:
36612  * Ext JS Library 1.1.1
36613  * Copyright(c) 2006-2007, Ext JS, LLC.
36614  *
36615  * Originally Released Under LGPL - original licence link has changed is not relivant.
36616  *
36617  * Fork - LGPL
36618  * <script type="text/javascript">
36619  */
36620
36621 /**
36622  * @class Roo.bootstrap.layout.Manager
36623  * @extends Roo.bootstrap.Component
36624  * Base class for layout managers.
36625  */
36626 Roo.bootstrap.layout.Manager = function(config)
36627 {
36628     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36629
36630
36631
36632
36633
36634     /** false to disable window resize monitoring @type Boolean */
36635     this.monitorWindowResize = true;
36636     this.regions = {};
36637     this.addEvents({
36638         /**
36639          * @event layout
36640          * Fires when a layout is performed.
36641          * @param {Roo.LayoutManager} this
36642          */
36643         "layout" : true,
36644         /**
36645          * @event regionresized
36646          * Fires when the user resizes a region.
36647          * @param {Roo.LayoutRegion} region The resized region
36648          * @param {Number} newSize The new size (width for east/west, height for north/south)
36649          */
36650         "regionresized" : true,
36651         /**
36652          * @event regioncollapsed
36653          * Fires when a region is collapsed.
36654          * @param {Roo.LayoutRegion} region The collapsed region
36655          */
36656         "regioncollapsed" : true,
36657         /**
36658          * @event regionexpanded
36659          * Fires when a region is expanded.
36660          * @param {Roo.LayoutRegion} region The expanded region
36661          */
36662         "regionexpanded" : true
36663     });
36664     this.updating = false;
36665
36666     if (config.el) {
36667         this.el = Roo.get(config.el);
36668         this.initEvents();
36669     }
36670
36671 };
36672
36673 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36674
36675
36676     regions : null,
36677
36678     monitorWindowResize : true,
36679
36680
36681     updating : false,
36682
36683
36684     onRender : function(ct, position)
36685     {
36686         if(!this.el){
36687             this.el = Roo.get(ct);
36688             this.initEvents();
36689         }
36690         //this.fireEvent('render',this);
36691     },
36692
36693
36694     initEvents: function()
36695     {
36696
36697
36698         // ie scrollbar fix
36699         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36700             document.body.scroll = "no";
36701         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36702             this.el.position('relative');
36703         }
36704         this.id = this.el.id;
36705         this.el.addClass("roo-layout-container");
36706         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36707         if(this.el.dom != document.body ) {
36708             this.el.on('resize', this.layout,this);
36709             this.el.on('show', this.layout,this);
36710         }
36711
36712     },
36713
36714     /**
36715      * Returns true if this layout is currently being updated
36716      * @return {Boolean}
36717      */
36718     isUpdating : function(){
36719         return this.updating;
36720     },
36721
36722     /**
36723      * Suspend the LayoutManager from doing auto-layouts while
36724      * making multiple add or remove calls
36725      */
36726     beginUpdate : function(){
36727         this.updating = true;
36728     },
36729
36730     /**
36731      * Restore auto-layouts and optionally disable the manager from performing a layout
36732      * @param {Boolean} noLayout true to disable a layout update
36733      */
36734     endUpdate : function(noLayout){
36735         this.updating = false;
36736         if(!noLayout){
36737             this.layout();
36738         }
36739     },
36740
36741     layout: function(){
36742         // abstract...
36743     },
36744
36745     onRegionResized : function(region, newSize){
36746         this.fireEvent("regionresized", region, newSize);
36747         this.layout();
36748     },
36749
36750     onRegionCollapsed : function(region){
36751         this.fireEvent("regioncollapsed", region);
36752     },
36753
36754     onRegionExpanded : function(region){
36755         this.fireEvent("regionexpanded", region);
36756     },
36757
36758     /**
36759      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36760      * performs box-model adjustments.
36761      * @return {Object} The size as an object {width: (the width), height: (the height)}
36762      */
36763     getViewSize : function()
36764     {
36765         var size;
36766         if(this.el.dom != document.body){
36767             size = this.el.getSize();
36768         }else{
36769             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36770         }
36771         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36772         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36773         return size;
36774     },
36775
36776     /**
36777      * Returns the Element this layout is bound to.
36778      * @return {Roo.Element}
36779      */
36780     getEl : function(){
36781         return this.el;
36782     },
36783
36784     /**
36785      * Returns the specified region.
36786      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36787      * @return {Roo.LayoutRegion}
36788      */
36789     getRegion : function(target){
36790         return this.regions[target.toLowerCase()];
36791     },
36792
36793     onWindowResize : function(){
36794         if(this.monitorWindowResize){
36795             this.layout();
36796         }
36797     }
36798 });
36799 /*
36800  * Based on:
36801  * Ext JS Library 1.1.1
36802  * Copyright(c) 2006-2007, Ext JS, LLC.
36803  *
36804  * Originally Released Under LGPL - original licence link has changed is not relivant.
36805  *
36806  * Fork - LGPL
36807  * <script type="text/javascript">
36808  */
36809 /**
36810  * @class Roo.bootstrap.layout.Border
36811  * @extends Roo.bootstrap.layout.Manager
36812  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36813  * please see: examples/bootstrap/nested.html<br><br>
36814  
36815 <b>The container the layout is rendered into can be either the body element or any other element.
36816 If it is not the body element, the container needs to either be an absolute positioned element,
36817 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36818 the container size if it is not the body element.</b>
36819
36820 * @constructor
36821 * Create a new Border
36822 * @param {Object} config Configuration options
36823  */
36824 Roo.bootstrap.layout.Border = function(config){
36825     config = config || {};
36826     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36827     
36828     
36829     
36830     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36831         if(config[region]){
36832             config[region].region = region;
36833             this.addRegion(config[region]);
36834         }
36835     },this);
36836     
36837 };
36838
36839 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36840
36841 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36842     
36843     parent : false, // this might point to a 'nest' or a ???
36844     
36845     /**
36846      * Creates and adds a new region if it doesn't already exist.
36847      * @param {String} target The target region key (north, south, east, west or center).
36848      * @param {Object} config The regions config object
36849      * @return {BorderLayoutRegion} The new region
36850      */
36851     addRegion : function(config)
36852     {
36853         if(!this.regions[config.region]){
36854             var r = this.factory(config);
36855             this.bindRegion(r);
36856         }
36857         return this.regions[config.region];
36858     },
36859
36860     // private (kinda)
36861     bindRegion : function(r){
36862         this.regions[r.config.region] = r;
36863         
36864         r.on("visibilitychange",    this.layout, this);
36865         r.on("paneladded",          this.layout, this);
36866         r.on("panelremoved",        this.layout, this);
36867         r.on("invalidated",         this.layout, this);
36868         r.on("resized",             this.onRegionResized, this);
36869         r.on("collapsed",           this.onRegionCollapsed, this);
36870         r.on("expanded",            this.onRegionExpanded, this);
36871     },
36872
36873     /**
36874      * Performs a layout update.
36875      */
36876     layout : function()
36877     {
36878         if(this.updating) {
36879             return;
36880         }
36881         
36882         // render all the rebions if they have not been done alreayd?
36883         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36884             if(this.regions[region] && !this.regions[region].bodyEl){
36885                 this.regions[region].onRender(this.el)
36886             }
36887         },this);
36888         
36889         var size = this.getViewSize();
36890         var w = size.width;
36891         var h = size.height;
36892         var centerW = w;
36893         var centerH = h;
36894         var centerY = 0;
36895         var centerX = 0;
36896         //var x = 0, y = 0;
36897
36898         var rs = this.regions;
36899         var north = rs["north"];
36900         var south = rs["south"]; 
36901         var west = rs["west"];
36902         var east = rs["east"];
36903         var center = rs["center"];
36904         //if(this.hideOnLayout){ // not supported anymore
36905             //c.el.setStyle("display", "none");
36906         //}
36907         if(north && north.isVisible()){
36908             var b = north.getBox();
36909             var m = north.getMargins();
36910             b.width = w - (m.left+m.right);
36911             b.x = m.left;
36912             b.y = m.top;
36913             centerY = b.height + b.y + m.bottom;
36914             centerH -= centerY;
36915             north.updateBox(this.safeBox(b));
36916         }
36917         if(south && south.isVisible()){
36918             var b = south.getBox();
36919             var m = south.getMargins();
36920             b.width = w - (m.left+m.right);
36921             b.x = m.left;
36922             var totalHeight = (b.height + m.top + m.bottom);
36923             b.y = h - totalHeight + m.top;
36924             centerH -= totalHeight;
36925             south.updateBox(this.safeBox(b));
36926         }
36927         if(west && west.isVisible()){
36928             var b = west.getBox();
36929             var m = west.getMargins();
36930             b.height = centerH - (m.top+m.bottom);
36931             b.x = m.left;
36932             b.y = centerY + m.top;
36933             var totalWidth = (b.width + m.left + m.right);
36934             centerX += totalWidth;
36935             centerW -= totalWidth;
36936             west.updateBox(this.safeBox(b));
36937         }
36938         if(east && east.isVisible()){
36939             var b = east.getBox();
36940             var m = east.getMargins();
36941             b.height = centerH - (m.top+m.bottom);
36942             var totalWidth = (b.width + m.left + m.right);
36943             b.x = w - totalWidth + m.left;
36944             b.y = centerY + m.top;
36945             centerW -= totalWidth;
36946             east.updateBox(this.safeBox(b));
36947         }
36948         if(center){
36949             var m = center.getMargins();
36950             var centerBox = {
36951                 x: centerX + m.left,
36952                 y: centerY + m.top,
36953                 width: centerW - (m.left+m.right),
36954                 height: centerH - (m.top+m.bottom)
36955             };
36956             //if(this.hideOnLayout){
36957                 //center.el.setStyle("display", "block");
36958             //}
36959             center.updateBox(this.safeBox(centerBox));
36960         }
36961         this.el.repaint();
36962         this.fireEvent("layout", this);
36963     },
36964
36965     // private
36966     safeBox : function(box){
36967         box.width = Math.max(0, box.width);
36968         box.height = Math.max(0, box.height);
36969         return box;
36970     },
36971
36972     /**
36973      * Adds a ContentPanel (or subclass) to this layout.
36974      * @param {String} target The target region key (north, south, east, west or center).
36975      * @param {Roo.ContentPanel} panel The panel to add
36976      * @return {Roo.ContentPanel} The added panel
36977      */
36978     add : function(target, panel){
36979          
36980         target = target.toLowerCase();
36981         return this.regions[target].add(panel);
36982     },
36983
36984     /**
36985      * Remove a ContentPanel (or subclass) to this layout.
36986      * @param {String} target The target region key (north, south, east, west or center).
36987      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
36988      * @return {Roo.ContentPanel} The removed panel
36989      */
36990     remove : function(target, panel){
36991         target = target.toLowerCase();
36992         return this.regions[target].remove(panel);
36993     },
36994
36995     /**
36996      * Searches all regions for a panel with the specified id
36997      * @param {String} panelId
36998      * @return {Roo.ContentPanel} The panel or null if it wasn't found
36999      */
37000     findPanel : function(panelId){
37001         var rs = this.regions;
37002         for(var target in rs){
37003             if(typeof rs[target] != "function"){
37004                 var p = rs[target].getPanel(panelId);
37005                 if(p){
37006                     return p;
37007                 }
37008             }
37009         }
37010         return null;
37011     },
37012
37013     /**
37014      * Searches all regions for a panel with the specified id and activates (shows) it.
37015      * @param {String/ContentPanel} panelId The panels id or the panel itself
37016      * @return {Roo.ContentPanel} The shown panel or null
37017      */
37018     showPanel : function(panelId) {
37019       var rs = this.regions;
37020       for(var target in rs){
37021          var r = rs[target];
37022          if(typeof r != "function"){
37023             if(r.hasPanel(panelId)){
37024                return r.showPanel(panelId);
37025             }
37026          }
37027       }
37028       return null;
37029    },
37030
37031    /**
37032      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37033      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37034      */
37035    /*
37036     restoreState : function(provider){
37037         if(!provider){
37038             provider = Roo.state.Manager;
37039         }
37040         var sm = new Roo.LayoutStateManager();
37041         sm.init(this, provider);
37042     },
37043 */
37044  
37045  
37046     /**
37047      * Adds a xtype elements to the layout.
37048      * <pre><code>
37049
37050 layout.addxtype({
37051        xtype : 'ContentPanel',
37052        region: 'west',
37053        items: [ .... ]
37054    }
37055 );
37056
37057 layout.addxtype({
37058         xtype : 'NestedLayoutPanel',
37059         region: 'west',
37060         layout: {
37061            center: { },
37062            west: { }   
37063         },
37064         items : [ ... list of content panels or nested layout panels.. ]
37065    }
37066 );
37067 </code></pre>
37068      * @param {Object} cfg Xtype definition of item to add.
37069      */
37070     addxtype : function(cfg)
37071     {
37072         // basically accepts a pannel...
37073         // can accept a layout region..!?!?
37074         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37075         
37076         
37077         // theory?  children can only be panels??
37078         
37079         //if (!cfg.xtype.match(/Panel$/)) {
37080         //    return false;
37081         //}
37082         var ret = false;
37083         
37084         if (typeof(cfg.region) == 'undefined') {
37085             Roo.log("Failed to add Panel, region was not set");
37086             Roo.log(cfg);
37087             return false;
37088         }
37089         var region = cfg.region;
37090         delete cfg.region;
37091         
37092           
37093         var xitems = [];
37094         if (cfg.items) {
37095             xitems = cfg.items;
37096             delete cfg.items;
37097         }
37098         var nb = false;
37099         
37100         if ( region == 'center') {
37101             Roo.log("Center: " + cfg.title);
37102         }
37103         
37104         
37105         switch(cfg.xtype) 
37106         {
37107             case 'Content':  // ContentPanel (el, cfg)
37108             case 'Scroll':  // ContentPanel (el, cfg)
37109             case 'View': 
37110                 cfg.autoCreate = cfg.autoCreate || true;
37111                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37112                 //} else {
37113                 //    var el = this.el.createChild();
37114                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37115                 //}
37116                 
37117                 this.add(region, ret);
37118                 break;
37119             
37120             /*
37121             case 'TreePanel': // our new panel!
37122                 cfg.el = this.el.createChild();
37123                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37124                 this.add(region, ret);
37125                 break;
37126             */
37127             
37128             case 'Nest': 
37129                 // create a new Layout (which is  a Border Layout...
37130                 
37131                 var clayout = cfg.layout;
37132                 clayout.el  = this.el.createChild();
37133                 clayout.items   = clayout.items  || [];
37134                 
37135                 delete cfg.layout;
37136                 
37137                 // replace this exitems with the clayout ones..
37138                 xitems = clayout.items;
37139                  
37140                 // force background off if it's in center...
37141                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37142                     cfg.background = false;
37143                 }
37144                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37145                 
37146                 
37147                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37148                 //console.log('adding nested layout panel '  + cfg.toSource());
37149                 this.add(region, ret);
37150                 nb = {}; /// find first...
37151                 break;
37152             
37153             case 'Grid':
37154                 
37155                 // needs grid and region
37156                 
37157                 //var el = this.getRegion(region).el.createChild();
37158                 /*
37159                  *var el = this.el.createChild();
37160                 // create the grid first...
37161                 cfg.grid.container = el;
37162                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37163                 */
37164                 
37165                 if (region == 'center' && this.active ) {
37166                     cfg.background = false;
37167                 }
37168                 
37169                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37170                 
37171                 this.add(region, ret);
37172                 /*
37173                 if (cfg.background) {
37174                     // render grid on panel activation (if panel background)
37175                     ret.on('activate', function(gp) {
37176                         if (!gp.grid.rendered) {
37177                     //        gp.grid.render(el);
37178                         }
37179                     });
37180                 } else {
37181                   //  cfg.grid.render(el);
37182                 }
37183                 */
37184                 break;
37185            
37186            
37187             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37188                 // it was the old xcomponent building that caused this before.
37189                 // espeically if border is the top element in the tree.
37190                 ret = this;
37191                 break; 
37192                 
37193                     
37194                 
37195                 
37196                 
37197             default:
37198                 /*
37199                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37200                     
37201                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37202                     this.add(region, ret);
37203                 } else {
37204                 */
37205                     Roo.log(cfg);
37206                     throw "Can not add '" + cfg.xtype + "' to Border";
37207                     return null;
37208              
37209                                 
37210              
37211         }
37212         this.beginUpdate();
37213         // add children..
37214         var region = '';
37215         var abn = {};
37216         Roo.each(xitems, function(i)  {
37217             region = nb && i.region ? i.region : false;
37218             
37219             var add = ret.addxtype(i);
37220            
37221             if (region) {
37222                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37223                 if (!i.background) {
37224                     abn[region] = nb[region] ;
37225                 }
37226             }
37227             
37228         });
37229         this.endUpdate();
37230
37231         // make the last non-background panel active..
37232         //if (nb) { Roo.log(abn); }
37233         if (nb) {
37234             
37235             for(var r in abn) {
37236                 region = this.getRegion(r);
37237                 if (region) {
37238                     // tried using nb[r], but it does not work..
37239                      
37240                     region.showPanel(abn[r]);
37241                    
37242                 }
37243             }
37244         }
37245         return ret;
37246         
37247     },
37248     
37249     
37250 // private
37251     factory : function(cfg)
37252     {
37253         
37254         var validRegions = Roo.bootstrap.layout.Border.regions;
37255
37256         var target = cfg.region;
37257         cfg.mgr = this;
37258         
37259         var r = Roo.bootstrap.layout;
37260         Roo.log(target);
37261         switch(target){
37262             case "north":
37263                 return new r.North(cfg);
37264             case "south":
37265                 return new r.South(cfg);
37266             case "east":
37267                 return new r.East(cfg);
37268             case "west":
37269                 return new r.West(cfg);
37270             case "center":
37271                 return new r.Center(cfg);
37272         }
37273         throw 'Layout region "'+target+'" not supported.';
37274     }
37275     
37276     
37277 });
37278  /*
37279  * Based on:
37280  * Ext JS Library 1.1.1
37281  * Copyright(c) 2006-2007, Ext JS, LLC.
37282  *
37283  * Originally Released Under LGPL - original licence link has changed is not relivant.
37284  *
37285  * Fork - LGPL
37286  * <script type="text/javascript">
37287  */
37288  
37289 /**
37290  * @class Roo.bootstrap.layout.Basic
37291  * @extends Roo.util.Observable
37292  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37293  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37294  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37295  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37296  * @cfg {string}   region  the region that it inhabits..
37297  * @cfg {bool}   skipConfig skip config?
37298  * 
37299
37300  */
37301 Roo.bootstrap.layout.Basic = function(config){
37302     
37303     this.mgr = config.mgr;
37304     
37305     this.position = config.region;
37306     
37307     var skipConfig = config.skipConfig;
37308     
37309     this.events = {
37310         /**
37311          * @scope Roo.BasicLayoutRegion
37312          */
37313         
37314         /**
37315          * @event beforeremove
37316          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37317          * @param {Roo.LayoutRegion} this
37318          * @param {Roo.ContentPanel} panel The panel
37319          * @param {Object} e The cancel event object
37320          */
37321         "beforeremove" : true,
37322         /**
37323          * @event invalidated
37324          * Fires when the layout for this region is changed.
37325          * @param {Roo.LayoutRegion} this
37326          */
37327         "invalidated" : true,
37328         /**
37329          * @event visibilitychange
37330          * Fires when this region is shown or hidden 
37331          * @param {Roo.LayoutRegion} this
37332          * @param {Boolean} visibility true or false
37333          */
37334         "visibilitychange" : true,
37335         /**
37336          * @event paneladded
37337          * Fires when a panel is added. 
37338          * @param {Roo.LayoutRegion} this
37339          * @param {Roo.ContentPanel} panel The panel
37340          */
37341         "paneladded" : true,
37342         /**
37343          * @event panelremoved
37344          * Fires when a panel is removed. 
37345          * @param {Roo.LayoutRegion} this
37346          * @param {Roo.ContentPanel} panel The panel
37347          */
37348         "panelremoved" : true,
37349         /**
37350          * @event beforecollapse
37351          * Fires when this region before collapse.
37352          * @param {Roo.LayoutRegion} this
37353          */
37354         "beforecollapse" : true,
37355         /**
37356          * @event collapsed
37357          * Fires when this region is collapsed.
37358          * @param {Roo.LayoutRegion} this
37359          */
37360         "collapsed" : true,
37361         /**
37362          * @event expanded
37363          * Fires when this region is expanded.
37364          * @param {Roo.LayoutRegion} this
37365          */
37366         "expanded" : true,
37367         /**
37368          * @event slideshow
37369          * Fires when this region is slid into view.
37370          * @param {Roo.LayoutRegion} this
37371          */
37372         "slideshow" : true,
37373         /**
37374          * @event slidehide
37375          * Fires when this region slides out of view. 
37376          * @param {Roo.LayoutRegion} this
37377          */
37378         "slidehide" : true,
37379         /**
37380          * @event panelactivated
37381          * Fires when a panel is activated. 
37382          * @param {Roo.LayoutRegion} this
37383          * @param {Roo.ContentPanel} panel The activated panel
37384          */
37385         "panelactivated" : true,
37386         /**
37387          * @event resized
37388          * Fires when the user resizes this region. 
37389          * @param {Roo.LayoutRegion} this
37390          * @param {Number} newSize The new size (width for east/west, height for north/south)
37391          */
37392         "resized" : true
37393     };
37394     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37395     this.panels = new Roo.util.MixedCollection();
37396     this.panels.getKey = this.getPanelId.createDelegate(this);
37397     this.box = null;
37398     this.activePanel = null;
37399     // ensure listeners are added...
37400     
37401     if (config.listeners || config.events) {
37402         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37403             listeners : config.listeners || {},
37404             events : config.events || {}
37405         });
37406     }
37407     
37408     if(skipConfig !== true){
37409         this.applyConfig(config);
37410     }
37411 };
37412
37413 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37414 {
37415     getPanelId : function(p){
37416         return p.getId();
37417     },
37418     
37419     applyConfig : function(config){
37420         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37421         this.config = config;
37422         
37423     },
37424     
37425     /**
37426      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37427      * the width, for horizontal (north, south) the height.
37428      * @param {Number} newSize The new width or height
37429      */
37430     resizeTo : function(newSize){
37431         var el = this.el ? this.el :
37432                  (this.activePanel ? this.activePanel.getEl() : null);
37433         if(el){
37434             switch(this.position){
37435                 case "east":
37436                 case "west":
37437                     el.setWidth(newSize);
37438                     this.fireEvent("resized", this, newSize);
37439                 break;
37440                 case "north":
37441                 case "south":
37442                     el.setHeight(newSize);
37443                     this.fireEvent("resized", this, newSize);
37444                 break;                
37445             }
37446         }
37447     },
37448     
37449     getBox : function(){
37450         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37451     },
37452     
37453     getMargins : function(){
37454         return this.margins;
37455     },
37456     
37457     updateBox : function(box){
37458         this.box = box;
37459         var el = this.activePanel.getEl();
37460         el.dom.style.left = box.x + "px";
37461         el.dom.style.top = box.y + "px";
37462         this.activePanel.setSize(box.width, box.height);
37463     },
37464     
37465     /**
37466      * Returns the container element for this region.
37467      * @return {Roo.Element}
37468      */
37469     getEl : function(){
37470         return this.activePanel;
37471     },
37472     
37473     /**
37474      * Returns true if this region is currently visible.
37475      * @return {Boolean}
37476      */
37477     isVisible : function(){
37478         return this.activePanel ? true : false;
37479     },
37480     
37481     setActivePanel : function(panel){
37482         panel = this.getPanel(panel);
37483         if(this.activePanel && this.activePanel != panel){
37484             this.activePanel.setActiveState(false);
37485             this.activePanel.getEl().setLeftTop(-10000,-10000);
37486         }
37487         this.activePanel = panel;
37488         panel.setActiveState(true);
37489         if(this.box){
37490             panel.setSize(this.box.width, this.box.height);
37491         }
37492         this.fireEvent("panelactivated", this, panel);
37493         this.fireEvent("invalidated");
37494     },
37495     
37496     /**
37497      * Show the specified panel.
37498      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37499      * @return {Roo.ContentPanel} The shown panel or null
37500      */
37501     showPanel : function(panel){
37502         panel = this.getPanel(panel);
37503         if(panel){
37504             this.setActivePanel(panel);
37505         }
37506         return panel;
37507     },
37508     
37509     /**
37510      * Get the active panel for this region.
37511      * @return {Roo.ContentPanel} The active panel or null
37512      */
37513     getActivePanel : function(){
37514         return this.activePanel;
37515     },
37516     
37517     /**
37518      * Add the passed ContentPanel(s)
37519      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37520      * @return {Roo.ContentPanel} The panel added (if only one was added)
37521      */
37522     add : function(panel){
37523         if(arguments.length > 1){
37524             for(var i = 0, len = arguments.length; i < len; i++) {
37525                 this.add(arguments[i]);
37526             }
37527             return null;
37528         }
37529         if(this.hasPanel(panel)){
37530             this.showPanel(panel);
37531             return panel;
37532         }
37533         var el = panel.getEl();
37534         if(el.dom.parentNode != this.mgr.el.dom){
37535             this.mgr.el.dom.appendChild(el.dom);
37536         }
37537         if(panel.setRegion){
37538             panel.setRegion(this);
37539         }
37540         this.panels.add(panel);
37541         el.setStyle("position", "absolute");
37542         if(!panel.background){
37543             this.setActivePanel(panel);
37544             if(this.config.initialSize && this.panels.getCount()==1){
37545                 this.resizeTo(this.config.initialSize);
37546             }
37547         }
37548         this.fireEvent("paneladded", this, panel);
37549         return panel;
37550     },
37551     
37552     /**
37553      * Returns true if the panel is in this region.
37554      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37555      * @return {Boolean}
37556      */
37557     hasPanel : function(panel){
37558         if(typeof panel == "object"){ // must be panel obj
37559             panel = panel.getId();
37560         }
37561         return this.getPanel(panel) ? true : false;
37562     },
37563     
37564     /**
37565      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37566      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37567      * @param {Boolean} preservePanel Overrides the config preservePanel option
37568      * @return {Roo.ContentPanel} The panel that was removed
37569      */
37570     remove : function(panel, preservePanel){
37571         panel = this.getPanel(panel);
37572         if(!panel){
37573             return null;
37574         }
37575         var e = {};
37576         this.fireEvent("beforeremove", this, panel, e);
37577         if(e.cancel === true){
37578             return null;
37579         }
37580         var panelId = panel.getId();
37581         this.panels.removeKey(panelId);
37582         return panel;
37583     },
37584     
37585     /**
37586      * Returns the panel specified or null if it's not in this region.
37587      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37588      * @return {Roo.ContentPanel}
37589      */
37590     getPanel : function(id){
37591         if(typeof id == "object"){ // must be panel obj
37592             return id;
37593         }
37594         return this.panels.get(id);
37595     },
37596     
37597     /**
37598      * Returns this regions position (north/south/east/west/center).
37599      * @return {String} 
37600      */
37601     getPosition: function(){
37602         return this.position;    
37603     }
37604 });/*
37605  * Based on:
37606  * Ext JS Library 1.1.1
37607  * Copyright(c) 2006-2007, Ext JS, LLC.
37608  *
37609  * Originally Released Under LGPL - original licence link has changed is not relivant.
37610  *
37611  * Fork - LGPL
37612  * <script type="text/javascript">
37613  */
37614  
37615 /**
37616  * @class Roo.bootstrap.layout.Region
37617  * @extends Roo.bootstrap.layout.Basic
37618  * This class represents a region in a layout manager.
37619  
37620  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37621  * @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})
37622  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37623  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37624  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37625  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37626  * @cfg {String}    title           The title for the region (overrides panel titles)
37627  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37628  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37629  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37630  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37631  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37632  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37633  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37634  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37635  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37636  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37637
37638  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37639  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37640  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37641  * @cfg {Number}    width           For East/West panels
37642  * @cfg {Number}    height          For North/South panels
37643  * @cfg {Boolean}   split           To show the splitter
37644  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37645  * 
37646  * @cfg {string}   cls             Extra CSS classes to add to region
37647  * 
37648  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37649  * @cfg {string}   region  the region that it inhabits..
37650  *
37651
37652  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37653  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37654
37655  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37656  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37657  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37658  */
37659 Roo.bootstrap.layout.Region = function(config)
37660 {
37661     this.applyConfig(config);
37662
37663     var mgr = config.mgr;
37664     var pos = config.region;
37665     config.skipConfig = true;
37666     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37667     
37668     if (mgr.el) {
37669         this.onRender(mgr.el);   
37670     }
37671      
37672     this.visible = true;
37673     this.collapsed = false;
37674     this.unrendered_panels = [];
37675 };
37676
37677 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37678
37679     position: '', // set by wrapper (eg. north/south etc..)
37680     unrendered_panels : null,  // unrendered panels.
37681     
37682     tabPosition : false,
37683     
37684     mgr: false, // points to 'Border'
37685     
37686     
37687     createBody : function(){
37688         /** This region's body element 
37689         * @type Roo.Element */
37690         this.bodyEl = this.el.createChild({
37691                 tag: "div",
37692                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37693         });
37694     },
37695
37696     onRender: function(ctr, pos)
37697     {
37698         var dh = Roo.DomHelper;
37699         /** This region's container element 
37700         * @type Roo.Element */
37701         this.el = dh.append(ctr.dom, {
37702                 tag: "div",
37703                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37704             }, true);
37705         /** This region's title element 
37706         * @type Roo.Element */
37707     
37708         this.titleEl = dh.append(this.el.dom,  {
37709                 tag: "div",
37710                 unselectable: "on",
37711                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37712                 children:[
37713                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37714                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37715                 ]
37716             }, true);
37717         
37718         this.titleEl.enableDisplayMode();
37719         /** This region's title text element 
37720         * @type HTMLElement */
37721         this.titleTextEl = this.titleEl.dom.firstChild;
37722         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37723         /*
37724         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37725         this.closeBtn.enableDisplayMode();
37726         this.closeBtn.on("click", this.closeClicked, this);
37727         this.closeBtn.hide();
37728     */
37729         this.createBody(this.config);
37730         if(this.config.hideWhenEmpty){
37731             this.hide();
37732             this.on("paneladded", this.validateVisibility, this);
37733             this.on("panelremoved", this.validateVisibility, this);
37734         }
37735         if(this.autoScroll){
37736             this.bodyEl.setStyle("overflow", "auto");
37737         }else{
37738             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37739         }
37740         //if(c.titlebar !== false){
37741             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37742                 this.titleEl.hide();
37743             }else{
37744                 this.titleEl.show();
37745                 if(this.config.title){
37746                     this.titleTextEl.innerHTML = this.config.title;
37747                 }
37748             }
37749         //}
37750         if(this.config.collapsed){
37751             this.collapse(true);
37752         }
37753         if(this.config.hidden){
37754             this.hide();
37755         }
37756         
37757         if (this.unrendered_panels && this.unrendered_panels.length) {
37758             for (var i =0;i< this.unrendered_panels.length; i++) {
37759                 this.add(this.unrendered_panels[i]);
37760             }
37761             this.unrendered_panels = null;
37762             
37763         }
37764         
37765     },
37766     
37767     applyConfig : function(c)
37768     {
37769         /*
37770          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37771             var dh = Roo.DomHelper;
37772             if(c.titlebar !== false){
37773                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37774                 this.collapseBtn.on("click", this.collapse, this);
37775                 this.collapseBtn.enableDisplayMode();
37776                 /*
37777                 if(c.showPin === true || this.showPin){
37778                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37779                     this.stickBtn.enableDisplayMode();
37780                     this.stickBtn.on("click", this.expand, this);
37781                     this.stickBtn.hide();
37782                 }
37783                 
37784             }
37785             */
37786             /** This region's collapsed element
37787             * @type Roo.Element */
37788             /*
37789              *
37790             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37791                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37792             ]}, true);
37793             
37794             if(c.floatable !== false){
37795                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37796                this.collapsedEl.on("click", this.collapseClick, this);
37797             }
37798
37799             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37800                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37801                    id: "message", unselectable: "on", style:{"float":"left"}});
37802                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37803              }
37804             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37805             this.expandBtn.on("click", this.expand, this);
37806             
37807         }
37808         
37809         if(this.collapseBtn){
37810             this.collapseBtn.setVisible(c.collapsible == true);
37811         }
37812         
37813         this.cmargins = c.cmargins || this.cmargins ||
37814                          (this.position == "west" || this.position == "east" ?
37815                              {top: 0, left: 2, right:2, bottom: 0} :
37816                              {top: 2, left: 0, right:0, bottom: 2});
37817         */
37818         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37819         
37820         
37821         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37822         
37823         this.autoScroll = c.autoScroll || false;
37824         
37825         
37826        
37827         
37828         this.duration = c.duration || .30;
37829         this.slideDuration = c.slideDuration || .45;
37830         this.config = c;
37831        
37832     },
37833     /**
37834      * Returns true if this region is currently visible.
37835      * @return {Boolean}
37836      */
37837     isVisible : function(){
37838         return this.visible;
37839     },
37840
37841     /**
37842      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37843      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37844      */
37845     //setCollapsedTitle : function(title){
37846     //    title = title || "&#160;";
37847      //   if(this.collapsedTitleTextEl){
37848       //      this.collapsedTitleTextEl.innerHTML = title;
37849        // }
37850     //},
37851
37852     getBox : function(){
37853         var b;
37854       //  if(!this.collapsed){
37855             b = this.el.getBox(false, true);
37856        // }else{
37857           //  b = this.collapsedEl.getBox(false, true);
37858         //}
37859         return b;
37860     },
37861
37862     getMargins : function(){
37863         return this.margins;
37864         //return this.collapsed ? this.cmargins : this.margins;
37865     },
37866 /*
37867     highlight : function(){
37868         this.el.addClass("x-layout-panel-dragover");
37869     },
37870
37871     unhighlight : function(){
37872         this.el.removeClass("x-layout-panel-dragover");
37873     },
37874 */
37875     updateBox : function(box)
37876     {
37877         if (!this.bodyEl) {
37878             return; // not rendered yet..
37879         }
37880         
37881         this.box = box;
37882         if(!this.collapsed){
37883             this.el.dom.style.left = box.x + "px";
37884             this.el.dom.style.top = box.y + "px";
37885             this.updateBody(box.width, box.height);
37886         }else{
37887             this.collapsedEl.dom.style.left = box.x + "px";
37888             this.collapsedEl.dom.style.top = box.y + "px";
37889             this.collapsedEl.setSize(box.width, box.height);
37890         }
37891         if(this.tabs){
37892             this.tabs.autoSizeTabs();
37893         }
37894     },
37895
37896     updateBody : function(w, h)
37897     {
37898         if(w !== null){
37899             this.el.setWidth(w);
37900             w -= this.el.getBorderWidth("rl");
37901             if(this.config.adjustments){
37902                 w += this.config.adjustments[0];
37903             }
37904         }
37905         if(h !== null && h > 0){
37906             this.el.setHeight(h);
37907             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37908             h -= this.el.getBorderWidth("tb");
37909             if(this.config.adjustments){
37910                 h += this.config.adjustments[1];
37911             }
37912             this.bodyEl.setHeight(h);
37913             if(this.tabs){
37914                 h = this.tabs.syncHeight(h);
37915             }
37916         }
37917         if(this.panelSize){
37918             w = w !== null ? w : this.panelSize.width;
37919             h = h !== null ? h : this.panelSize.height;
37920         }
37921         if(this.activePanel){
37922             var el = this.activePanel.getEl();
37923             w = w !== null ? w : el.getWidth();
37924             h = h !== null ? h : el.getHeight();
37925             this.panelSize = {width: w, height: h};
37926             this.activePanel.setSize(w, h);
37927         }
37928         if(Roo.isIE && this.tabs){
37929             this.tabs.el.repaint();
37930         }
37931     },
37932
37933     /**
37934      * Returns the container element for this region.
37935      * @return {Roo.Element}
37936      */
37937     getEl : function(){
37938         return this.el;
37939     },
37940
37941     /**
37942      * Hides this region.
37943      */
37944     hide : function(){
37945         //if(!this.collapsed){
37946             this.el.dom.style.left = "-2000px";
37947             this.el.hide();
37948         //}else{
37949          //   this.collapsedEl.dom.style.left = "-2000px";
37950          //   this.collapsedEl.hide();
37951        // }
37952         this.visible = false;
37953         this.fireEvent("visibilitychange", this, false);
37954     },
37955
37956     /**
37957      * Shows this region if it was previously hidden.
37958      */
37959     show : function(){
37960         //if(!this.collapsed){
37961             this.el.show();
37962         //}else{
37963         //    this.collapsedEl.show();
37964        // }
37965         this.visible = true;
37966         this.fireEvent("visibilitychange", this, true);
37967     },
37968 /*
37969     closeClicked : function(){
37970         if(this.activePanel){
37971             this.remove(this.activePanel);
37972         }
37973     },
37974
37975     collapseClick : function(e){
37976         if(this.isSlid){
37977            e.stopPropagation();
37978            this.slideIn();
37979         }else{
37980            e.stopPropagation();
37981            this.slideOut();
37982         }
37983     },
37984 */
37985     /**
37986      * Collapses this region.
37987      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
37988      */
37989     /*
37990     collapse : function(skipAnim, skipCheck = false){
37991         if(this.collapsed) {
37992             return;
37993         }
37994         
37995         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
37996             
37997             this.collapsed = true;
37998             if(this.split){
37999                 this.split.el.hide();
38000             }
38001             if(this.config.animate && skipAnim !== true){
38002                 this.fireEvent("invalidated", this);
38003                 this.animateCollapse();
38004             }else{
38005                 this.el.setLocation(-20000,-20000);
38006                 this.el.hide();
38007                 this.collapsedEl.show();
38008                 this.fireEvent("collapsed", this);
38009                 this.fireEvent("invalidated", this);
38010             }
38011         }
38012         
38013     },
38014 */
38015     animateCollapse : function(){
38016         // overridden
38017     },
38018
38019     /**
38020      * Expands this region if it was previously collapsed.
38021      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38022      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38023      */
38024     /*
38025     expand : function(e, skipAnim){
38026         if(e) {
38027             e.stopPropagation();
38028         }
38029         if(!this.collapsed || this.el.hasActiveFx()) {
38030             return;
38031         }
38032         if(this.isSlid){
38033             this.afterSlideIn();
38034             skipAnim = true;
38035         }
38036         this.collapsed = false;
38037         if(this.config.animate && skipAnim !== true){
38038             this.animateExpand();
38039         }else{
38040             this.el.show();
38041             if(this.split){
38042                 this.split.el.show();
38043             }
38044             this.collapsedEl.setLocation(-2000,-2000);
38045             this.collapsedEl.hide();
38046             this.fireEvent("invalidated", this);
38047             this.fireEvent("expanded", this);
38048         }
38049     },
38050 */
38051     animateExpand : function(){
38052         // overridden
38053     },
38054
38055     initTabs : function()
38056     {
38057         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38058         
38059         var ts = new Roo.bootstrap.panel.Tabs({
38060             el: this.bodyEl.dom,
38061             region : this,
38062             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38063             disableTooltips: this.config.disableTabTips,
38064             toolbar : this.config.toolbar
38065         });
38066         
38067         if(this.config.hideTabs){
38068             ts.stripWrap.setDisplayed(false);
38069         }
38070         this.tabs = ts;
38071         ts.resizeTabs = this.config.resizeTabs === true;
38072         ts.minTabWidth = this.config.minTabWidth || 40;
38073         ts.maxTabWidth = this.config.maxTabWidth || 250;
38074         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38075         ts.monitorResize = false;
38076         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38077         ts.bodyEl.addClass('roo-layout-tabs-body');
38078         this.panels.each(this.initPanelAsTab, this);
38079     },
38080
38081     initPanelAsTab : function(panel){
38082         var ti = this.tabs.addTab(
38083             panel.getEl().id,
38084             panel.getTitle(),
38085             null,
38086             this.config.closeOnTab && panel.isClosable(),
38087             panel.tpl
38088         );
38089         if(panel.tabTip !== undefined){
38090             ti.setTooltip(panel.tabTip);
38091         }
38092         ti.on("activate", function(){
38093               this.setActivePanel(panel);
38094         }, this);
38095         
38096         if(this.config.closeOnTab){
38097             ti.on("beforeclose", function(t, e){
38098                 e.cancel = true;
38099                 this.remove(panel);
38100             }, this);
38101         }
38102         
38103         panel.tabItem = ti;
38104         
38105         return ti;
38106     },
38107
38108     updatePanelTitle : function(panel, title)
38109     {
38110         if(this.activePanel == panel){
38111             this.updateTitle(title);
38112         }
38113         if(this.tabs){
38114             var ti = this.tabs.getTab(panel.getEl().id);
38115             ti.setText(title);
38116             if(panel.tabTip !== undefined){
38117                 ti.setTooltip(panel.tabTip);
38118             }
38119         }
38120     },
38121
38122     updateTitle : function(title){
38123         if(this.titleTextEl && !this.config.title){
38124             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38125         }
38126     },
38127
38128     setActivePanel : function(panel)
38129     {
38130         panel = this.getPanel(panel);
38131         if(this.activePanel && this.activePanel != panel){
38132             if(this.activePanel.setActiveState(false) === false){
38133                 return;
38134             }
38135         }
38136         this.activePanel = panel;
38137         panel.setActiveState(true);
38138         if(this.panelSize){
38139             panel.setSize(this.panelSize.width, this.panelSize.height);
38140         }
38141         if(this.closeBtn){
38142             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38143         }
38144         this.updateTitle(panel.getTitle());
38145         if(this.tabs){
38146             this.fireEvent("invalidated", this);
38147         }
38148         this.fireEvent("panelactivated", this, panel);
38149     },
38150
38151     /**
38152      * Shows the specified panel.
38153      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38154      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38155      */
38156     showPanel : function(panel)
38157     {
38158         panel = this.getPanel(panel);
38159         if(panel){
38160             if(this.tabs){
38161                 var tab = this.tabs.getTab(panel.getEl().id);
38162                 if(tab.isHidden()){
38163                     this.tabs.unhideTab(tab.id);
38164                 }
38165                 tab.activate();
38166             }else{
38167                 this.setActivePanel(panel);
38168             }
38169         }
38170         return panel;
38171     },
38172
38173     /**
38174      * Get the active panel for this region.
38175      * @return {Roo.ContentPanel} The active panel or null
38176      */
38177     getActivePanel : function(){
38178         return this.activePanel;
38179     },
38180
38181     validateVisibility : function(){
38182         if(this.panels.getCount() < 1){
38183             this.updateTitle("&#160;");
38184             this.closeBtn.hide();
38185             this.hide();
38186         }else{
38187             if(!this.isVisible()){
38188                 this.show();
38189             }
38190         }
38191     },
38192
38193     /**
38194      * Adds the passed ContentPanel(s) to this region.
38195      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38196      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38197      */
38198     add : function(panel)
38199     {
38200         if(arguments.length > 1){
38201             for(var i = 0, len = arguments.length; i < len; i++) {
38202                 this.add(arguments[i]);
38203             }
38204             return null;
38205         }
38206         
38207         // if we have not been rendered yet, then we can not really do much of this..
38208         if (!this.bodyEl) {
38209             this.unrendered_panels.push(panel);
38210             return panel;
38211         }
38212         
38213         
38214         
38215         
38216         if(this.hasPanel(panel)){
38217             this.showPanel(panel);
38218             return panel;
38219         }
38220         panel.setRegion(this);
38221         this.panels.add(panel);
38222        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38223             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38224             // and hide them... ???
38225             this.bodyEl.dom.appendChild(panel.getEl().dom);
38226             if(panel.background !== true){
38227                 this.setActivePanel(panel);
38228             }
38229             this.fireEvent("paneladded", this, panel);
38230             return panel;
38231         }
38232         */
38233         if(!this.tabs){
38234             this.initTabs();
38235         }else{
38236             this.initPanelAsTab(panel);
38237         }
38238         
38239         
38240         if(panel.background !== true){
38241             this.tabs.activate(panel.getEl().id);
38242         }
38243         this.fireEvent("paneladded", this, panel);
38244         return panel;
38245     },
38246
38247     /**
38248      * Hides the tab for the specified panel.
38249      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38250      */
38251     hidePanel : function(panel){
38252         if(this.tabs && (panel = this.getPanel(panel))){
38253             this.tabs.hideTab(panel.getEl().id);
38254         }
38255     },
38256
38257     /**
38258      * Unhides the tab for a previously hidden panel.
38259      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38260      */
38261     unhidePanel : function(panel){
38262         if(this.tabs && (panel = this.getPanel(panel))){
38263             this.tabs.unhideTab(panel.getEl().id);
38264         }
38265     },
38266
38267     clearPanels : function(){
38268         while(this.panels.getCount() > 0){
38269              this.remove(this.panels.first());
38270         }
38271     },
38272
38273     /**
38274      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38275      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38276      * @param {Boolean} preservePanel Overrides the config preservePanel option
38277      * @return {Roo.ContentPanel} The panel that was removed
38278      */
38279     remove : function(panel, preservePanel)
38280     {
38281         panel = this.getPanel(panel);
38282         if(!panel){
38283             return null;
38284         }
38285         var e = {};
38286         this.fireEvent("beforeremove", this, panel, e);
38287         if(e.cancel === true){
38288             return null;
38289         }
38290         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38291         var panelId = panel.getId();
38292         this.panels.removeKey(panelId);
38293         if(preservePanel){
38294             document.body.appendChild(panel.getEl().dom);
38295         }
38296         if(this.tabs){
38297             this.tabs.removeTab(panel.getEl().id);
38298         }else if (!preservePanel){
38299             this.bodyEl.dom.removeChild(panel.getEl().dom);
38300         }
38301         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38302             var p = this.panels.first();
38303             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38304             tempEl.appendChild(p.getEl().dom);
38305             this.bodyEl.update("");
38306             this.bodyEl.dom.appendChild(p.getEl().dom);
38307             tempEl = null;
38308             this.updateTitle(p.getTitle());
38309             this.tabs = null;
38310             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38311             this.setActivePanel(p);
38312         }
38313         panel.setRegion(null);
38314         if(this.activePanel == panel){
38315             this.activePanel = null;
38316         }
38317         if(this.config.autoDestroy !== false && preservePanel !== true){
38318             try{panel.destroy();}catch(e){}
38319         }
38320         this.fireEvent("panelremoved", this, panel);
38321         return panel;
38322     },
38323
38324     /**
38325      * Returns the TabPanel component used by this region
38326      * @return {Roo.TabPanel}
38327      */
38328     getTabs : function(){
38329         return this.tabs;
38330     },
38331
38332     createTool : function(parentEl, className){
38333         var btn = Roo.DomHelper.append(parentEl, {
38334             tag: "div",
38335             cls: "x-layout-tools-button",
38336             children: [ {
38337                 tag: "div",
38338                 cls: "roo-layout-tools-button-inner " + className,
38339                 html: "&#160;"
38340             }]
38341         }, true);
38342         btn.addClassOnOver("roo-layout-tools-button-over");
38343         return btn;
38344     }
38345 });/*
38346  * Based on:
38347  * Ext JS Library 1.1.1
38348  * Copyright(c) 2006-2007, Ext JS, LLC.
38349  *
38350  * Originally Released Under LGPL - original licence link has changed is not relivant.
38351  *
38352  * Fork - LGPL
38353  * <script type="text/javascript">
38354  */
38355  
38356
38357
38358 /**
38359  * @class Roo.SplitLayoutRegion
38360  * @extends Roo.LayoutRegion
38361  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38362  */
38363 Roo.bootstrap.layout.Split = function(config){
38364     this.cursor = config.cursor;
38365     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38366 };
38367
38368 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38369 {
38370     splitTip : "Drag to resize.",
38371     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38372     useSplitTips : false,
38373
38374     applyConfig : function(config){
38375         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38376     },
38377     
38378     onRender : function(ctr,pos) {
38379         
38380         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38381         if(!this.config.split){
38382             return;
38383         }
38384         if(!this.split){
38385             
38386             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38387                             tag: "div",
38388                             id: this.el.id + "-split",
38389                             cls: "roo-layout-split roo-layout-split-"+this.position,
38390                             html: "&#160;"
38391             });
38392             /** The SplitBar for this region 
38393             * @type Roo.SplitBar */
38394             // does not exist yet...
38395             Roo.log([this.position, this.orientation]);
38396             
38397             this.split = new Roo.bootstrap.SplitBar({
38398                 dragElement : splitEl,
38399                 resizingElement: this.el,
38400                 orientation : this.orientation
38401             });
38402             
38403             this.split.on("moved", this.onSplitMove, this);
38404             this.split.useShim = this.config.useShim === true;
38405             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38406             if(this.useSplitTips){
38407                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38408             }
38409             //if(config.collapsible){
38410             //    this.split.el.on("dblclick", this.collapse,  this);
38411             //}
38412         }
38413         if(typeof this.config.minSize != "undefined"){
38414             this.split.minSize = this.config.minSize;
38415         }
38416         if(typeof this.config.maxSize != "undefined"){
38417             this.split.maxSize = this.config.maxSize;
38418         }
38419         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38420             this.hideSplitter();
38421         }
38422         
38423     },
38424
38425     getHMaxSize : function(){
38426          var cmax = this.config.maxSize || 10000;
38427          var center = this.mgr.getRegion("center");
38428          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38429     },
38430
38431     getVMaxSize : function(){
38432          var cmax = this.config.maxSize || 10000;
38433          var center = this.mgr.getRegion("center");
38434          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38435     },
38436
38437     onSplitMove : function(split, newSize){
38438         this.fireEvent("resized", this, newSize);
38439     },
38440     
38441     /** 
38442      * Returns the {@link Roo.SplitBar} for this region.
38443      * @return {Roo.SplitBar}
38444      */
38445     getSplitBar : function(){
38446         return this.split;
38447     },
38448     
38449     hide : function(){
38450         this.hideSplitter();
38451         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38452     },
38453
38454     hideSplitter : function(){
38455         if(this.split){
38456             this.split.el.setLocation(-2000,-2000);
38457             this.split.el.hide();
38458         }
38459     },
38460
38461     show : function(){
38462         if(this.split){
38463             this.split.el.show();
38464         }
38465         Roo.bootstrap.layout.Split.superclass.show.call(this);
38466     },
38467     
38468     beforeSlide: function(){
38469         if(Roo.isGecko){// firefox overflow auto bug workaround
38470             this.bodyEl.clip();
38471             if(this.tabs) {
38472                 this.tabs.bodyEl.clip();
38473             }
38474             if(this.activePanel){
38475                 this.activePanel.getEl().clip();
38476                 
38477                 if(this.activePanel.beforeSlide){
38478                     this.activePanel.beforeSlide();
38479                 }
38480             }
38481         }
38482     },
38483     
38484     afterSlide : function(){
38485         if(Roo.isGecko){// firefox overflow auto bug workaround
38486             this.bodyEl.unclip();
38487             if(this.tabs) {
38488                 this.tabs.bodyEl.unclip();
38489             }
38490             if(this.activePanel){
38491                 this.activePanel.getEl().unclip();
38492                 if(this.activePanel.afterSlide){
38493                     this.activePanel.afterSlide();
38494                 }
38495             }
38496         }
38497     },
38498
38499     initAutoHide : function(){
38500         if(this.autoHide !== false){
38501             if(!this.autoHideHd){
38502                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38503                 this.autoHideHd = {
38504                     "mouseout": function(e){
38505                         if(!e.within(this.el, true)){
38506                             st.delay(500);
38507                         }
38508                     },
38509                     "mouseover" : function(e){
38510                         st.cancel();
38511                     },
38512                     scope : this
38513                 };
38514             }
38515             this.el.on(this.autoHideHd);
38516         }
38517     },
38518
38519     clearAutoHide : function(){
38520         if(this.autoHide !== false){
38521             this.el.un("mouseout", this.autoHideHd.mouseout);
38522             this.el.un("mouseover", this.autoHideHd.mouseover);
38523         }
38524     },
38525
38526     clearMonitor : function(){
38527         Roo.get(document).un("click", this.slideInIf, this);
38528     },
38529
38530     // these names are backwards but not changed for compat
38531     slideOut : function(){
38532         if(this.isSlid || this.el.hasActiveFx()){
38533             return;
38534         }
38535         this.isSlid = true;
38536         if(this.collapseBtn){
38537             this.collapseBtn.hide();
38538         }
38539         this.closeBtnState = this.closeBtn.getStyle('display');
38540         this.closeBtn.hide();
38541         if(this.stickBtn){
38542             this.stickBtn.show();
38543         }
38544         this.el.show();
38545         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38546         this.beforeSlide();
38547         this.el.setStyle("z-index", 10001);
38548         this.el.slideIn(this.getSlideAnchor(), {
38549             callback: function(){
38550                 this.afterSlide();
38551                 this.initAutoHide();
38552                 Roo.get(document).on("click", this.slideInIf, this);
38553                 this.fireEvent("slideshow", this);
38554             },
38555             scope: this,
38556             block: true
38557         });
38558     },
38559
38560     afterSlideIn : function(){
38561         this.clearAutoHide();
38562         this.isSlid = false;
38563         this.clearMonitor();
38564         this.el.setStyle("z-index", "");
38565         if(this.collapseBtn){
38566             this.collapseBtn.show();
38567         }
38568         this.closeBtn.setStyle('display', this.closeBtnState);
38569         if(this.stickBtn){
38570             this.stickBtn.hide();
38571         }
38572         this.fireEvent("slidehide", this);
38573     },
38574
38575     slideIn : function(cb){
38576         if(!this.isSlid || this.el.hasActiveFx()){
38577             Roo.callback(cb);
38578             return;
38579         }
38580         this.isSlid = false;
38581         this.beforeSlide();
38582         this.el.slideOut(this.getSlideAnchor(), {
38583             callback: function(){
38584                 this.el.setLeftTop(-10000, -10000);
38585                 this.afterSlide();
38586                 this.afterSlideIn();
38587                 Roo.callback(cb);
38588             },
38589             scope: this,
38590             block: true
38591         });
38592     },
38593     
38594     slideInIf : function(e){
38595         if(!e.within(this.el)){
38596             this.slideIn();
38597         }
38598     },
38599
38600     animateCollapse : function(){
38601         this.beforeSlide();
38602         this.el.setStyle("z-index", 20000);
38603         var anchor = this.getSlideAnchor();
38604         this.el.slideOut(anchor, {
38605             callback : function(){
38606                 this.el.setStyle("z-index", "");
38607                 this.collapsedEl.slideIn(anchor, {duration:.3});
38608                 this.afterSlide();
38609                 this.el.setLocation(-10000,-10000);
38610                 this.el.hide();
38611                 this.fireEvent("collapsed", this);
38612             },
38613             scope: this,
38614             block: true
38615         });
38616     },
38617
38618     animateExpand : function(){
38619         this.beforeSlide();
38620         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38621         this.el.setStyle("z-index", 20000);
38622         this.collapsedEl.hide({
38623             duration:.1
38624         });
38625         this.el.slideIn(this.getSlideAnchor(), {
38626             callback : function(){
38627                 this.el.setStyle("z-index", "");
38628                 this.afterSlide();
38629                 if(this.split){
38630                     this.split.el.show();
38631                 }
38632                 this.fireEvent("invalidated", this);
38633                 this.fireEvent("expanded", this);
38634             },
38635             scope: this,
38636             block: true
38637         });
38638     },
38639
38640     anchors : {
38641         "west" : "left",
38642         "east" : "right",
38643         "north" : "top",
38644         "south" : "bottom"
38645     },
38646
38647     sanchors : {
38648         "west" : "l",
38649         "east" : "r",
38650         "north" : "t",
38651         "south" : "b"
38652     },
38653
38654     canchors : {
38655         "west" : "tl-tr",
38656         "east" : "tr-tl",
38657         "north" : "tl-bl",
38658         "south" : "bl-tl"
38659     },
38660
38661     getAnchor : function(){
38662         return this.anchors[this.position];
38663     },
38664
38665     getCollapseAnchor : function(){
38666         return this.canchors[this.position];
38667     },
38668
38669     getSlideAnchor : function(){
38670         return this.sanchors[this.position];
38671     },
38672
38673     getAlignAdj : function(){
38674         var cm = this.cmargins;
38675         switch(this.position){
38676             case "west":
38677                 return [0, 0];
38678             break;
38679             case "east":
38680                 return [0, 0];
38681             break;
38682             case "north":
38683                 return [0, 0];
38684             break;
38685             case "south":
38686                 return [0, 0];
38687             break;
38688         }
38689     },
38690
38691     getExpandAdj : function(){
38692         var c = this.collapsedEl, cm = this.cmargins;
38693         switch(this.position){
38694             case "west":
38695                 return [-(cm.right+c.getWidth()+cm.left), 0];
38696             break;
38697             case "east":
38698                 return [cm.right+c.getWidth()+cm.left, 0];
38699             break;
38700             case "north":
38701                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38702             break;
38703             case "south":
38704                 return [0, cm.top+cm.bottom+c.getHeight()];
38705             break;
38706         }
38707     }
38708 });/*
38709  * Based on:
38710  * Ext JS Library 1.1.1
38711  * Copyright(c) 2006-2007, Ext JS, LLC.
38712  *
38713  * Originally Released Under LGPL - original licence link has changed is not relivant.
38714  *
38715  * Fork - LGPL
38716  * <script type="text/javascript">
38717  */
38718 /*
38719  * These classes are private internal classes
38720  */
38721 Roo.bootstrap.layout.Center = function(config){
38722     config.region = "center";
38723     Roo.bootstrap.layout.Region.call(this, config);
38724     this.visible = true;
38725     this.minWidth = config.minWidth || 20;
38726     this.minHeight = config.minHeight || 20;
38727 };
38728
38729 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38730     hide : function(){
38731         // center panel can't be hidden
38732     },
38733     
38734     show : function(){
38735         // center panel can't be hidden
38736     },
38737     
38738     getMinWidth: function(){
38739         return this.minWidth;
38740     },
38741     
38742     getMinHeight: function(){
38743         return this.minHeight;
38744     }
38745 });
38746
38747
38748
38749
38750  
38751
38752
38753
38754
38755
38756
38757 Roo.bootstrap.layout.North = function(config)
38758 {
38759     config.region = 'north';
38760     config.cursor = 'n-resize';
38761     
38762     Roo.bootstrap.layout.Split.call(this, config);
38763     
38764     
38765     if(this.split){
38766         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38767         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38768         this.split.el.addClass("roo-layout-split-v");
38769     }
38770     var size = config.initialSize || config.height;
38771     if(typeof size != "undefined"){
38772         this.el.setHeight(size);
38773     }
38774 };
38775 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38776 {
38777     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38778     
38779     
38780     
38781     getBox : function(){
38782         if(this.collapsed){
38783             return this.collapsedEl.getBox();
38784         }
38785         var box = this.el.getBox();
38786         if(this.split){
38787             box.height += this.split.el.getHeight();
38788         }
38789         return box;
38790     },
38791     
38792     updateBox : function(box){
38793         if(this.split && !this.collapsed){
38794             box.height -= this.split.el.getHeight();
38795             this.split.el.setLeft(box.x);
38796             this.split.el.setTop(box.y+box.height);
38797             this.split.el.setWidth(box.width);
38798         }
38799         if(this.collapsed){
38800             this.updateBody(box.width, null);
38801         }
38802         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38803     }
38804 });
38805
38806
38807
38808
38809
38810 Roo.bootstrap.layout.South = function(config){
38811     config.region = 'south';
38812     config.cursor = 's-resize';
38813     Roo.bootstrap.layout.Split.call(this, config);
38814     if(this.split){
38815         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38816         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38817         this.split.el.addClass("roo-layout-split-v");
38818     }
38819     var size = config.initialSize || config.height;
38820     if(typeof size != "undefined"){
38821         this.el.setHeight(size);
38822     }
38823 };
38824
38825 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38826     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38827     getBox : function(){
38828         if(this.collapsed){
38829             return this.collapsedEl.getBox();
38830         }
38831         var box = this.el.getBox();
38832         if(this.split){
38833             var sh = this.split.el.getHeight();
38834             box.height += sh;
38835             box.y -= sh;
38836         }
38837         return box;
38838     },
38839     
38840     updateBox : function(box){
38841         if(this.split && !this.collapsed){
38842             var sh = this.split.el.getHeight();
38843             box.height -= sh;
38844             box.y += sh;
38845             this.split.el.setLeft(box.x);
38846             this.split.el.setTop(box.y-sh);
38847             this.split.el.setWidth(box.width);
38848         }
38849         if(this.collapsed){
38850             this.updateBody(box.width, null);
38851         }
38852         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38853     }
38854 });
38855
38856 Roo.bootstrap.layout.East = function(config){
38857     config.region = "east";
38858     config.cursor = "e-resize";
38859     Roo.bootstrap.layout.Split.call(this, config);
38860     if(this.split){
38861         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38862         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38863         this.split.el.addClass("roo-layout-split-h");
38864     }
38865     var size = config.initialSize || config.width;
38866     if(typeof size != "undefined"){
38867         this.el.setWidth(size);
38868     }
38869 };
38870 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38871     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38872     getBox : function(){
38873         if(this.collapsed){
38874             return this.collapsedEl.getBox();
38875         }
38876         var box = this.el.getBox();
38877         if(this.split){
38878             var sw = this.split.el.getWidth();
38879             box.width += sw;
38880             box.x -= sw;
38881         }
38882         return box;
38883     },
38884
38885     updateBox : function(box){
38886         if(this.split && !this.collapsed){
38887             var sw = this.split.el.getWidth();
38888             box.width -= sw;
38889             this.split.el.setLeft(box.x);
38890             this.split.el.setTop(box.y);
38891             this.split.el.setHeight(box.height);
38892             box.x += sw;
38893         }
38894         if(this.collapsed){
38895             this.updateBody(null, box.height);
38896         }
38897         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38898     }
38899 });
38900
38901 Roo.bootstrap.layout.West = function(config){
38902     config.region = "west";
38903     config.cursor = "w-resize";
38904     
38905     Roo.bootstrap.layout.Split.call(this, config);
38906     if(this.split){
38907         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38908         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38909         this.split.el.addClass("roo-layout-split-h");
38910     }
38911     
38912 };
38913 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38914     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38915     
38916     onRender: function(ctr, pos)
38917     {
38918         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38919         var size = this.config.initialSize || this.config.width;
38920         if(typeof size != "undefined"){
38921             this.el.setWidth(size);
38922         }
38923     },
38924     
38925     getBox : function(){
38926         if(this.collapsed){
38927             return this.collapsedEl.getBox();
38928         }
38929         var box = this.el.getBox();
38930         if(this.split){
38931             box.width += this.split.el.getWidth();
38932         }
38933         return box;
38934     },
38935     
38936     updateBox : function(box){
38937         if(this.split && !this.collapsed){
38938             var sw = this.split.el.getWidth();
38939             box.width -= sw;
38940             this.split.el.setLeft(box.x+box.width);
38941             this.split.el.setTop(box.y);
38942             this.split.el.setHeight(box.height);
38943         }
38944         if(this.collapsed){
38945             this.updateBody(null, box.height);
38946         }
38947         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38948     }
38949 });Roo.namespace("Roo.bootstrap.panel");/*
38950  * Based on:
38951  * Ext JS Library 1.1.1
38952  * Copyright(c) 2006-2007, Ext JS, LLC.
38953  *
38954  * Originally Released Under LGPL - original licence link has changed is not relivant.
38955  *
38956  * Fork - LGPL
38957  * <script type="text/javascript">
38958  */
38959 /**
38960  * @class Roo.ContentPanel
38961  * @extends Roo.util.Observable
38962  * A basic ContentPanel element.
38963  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38964  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38965  * @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
38966  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38967  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38968  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38969  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38970  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38971  * @cfg {String} title          The title for this panel
38972  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38973  * @cfg {String} url            Calls {@link #setUrl} with this value
38974  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38975  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38976  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38977  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38978  * @cfg {Boolean} badges render the badges
38979
38980  * @constructor
38981  * Create a new ContentPanel.
38982  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
38983  * @param {String/Object} config A string to set only the title or a config object
38984  * @param {String} content (optional) Set the HTML content for this panel
38985  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
38986  */
38987 Roo.bootstrap.panel.Content = function( config){
38988     
38989     this.tpl = config.tpl || false;
38990     
38991     var el = config.el;
38992     var content = config.content;
38993
38994     if(config.autoCreate){ // xtype is available if this is called from factory
38995         el = Roo.id();
38996     }
38997     this.el = Roo.get(el);
38998     if(!this.el && config && config.autoCreate){
38999         if(typeof config.autoCreate == "object"){
39000             if(!config.autoCreate.id){
39001                 config.autoCreate.id = config.id||el;
39002             }
39003             this.el = Roo.DomHelper.append(document.body,
39004                         config.autoCreate, true);
39005         }else{
39006             var elcfg =  {   tag: "div",
39007                             cls: "roo-layout-inactive-content",
39008                             id: config.id||el
39009                             };
39010             if (config.html) {
39011                 elcfg.html = config.html;
39012                 
39013             }
39014                         
39015             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39016         }
39017     } 
39018     this.closable = false;
39019     this.loaded = false;
39020     this.active = false;
39021    
39022       
39023     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39024         
39025         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39026         
39027         this.wrapEl = this.el; //this.el.wrap();
39028         var ti = [];
39029         if (config.toolbar.items) {
39030             ti = config.toolbar.items ;
39031             delete config.toolbar.items ;
39032         }
39033         
39034         var nitems = [];
39035         this.toolbar.render(this.wrapEl, 'before');
39036         for(var i =0;i < ti.length;i++) {
39037           //  Roo.log(['add child', items[i]]);
39038             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39039         }
39040         this.toolbar.items = nitems;
39041         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39042         delete config.toolbar;
39043         
39044     }
39045     /*
39046     // xtype created footer. - not sure if will work as we normally have to render first..
39047     if (this.footer && !this.footer.el && this.footer.xtype) {
39048         if (!this.wrapEl) {
39049             this.wrapEl = this.el.wrap();
39050         }
39051     
39052         this.footer.container = this.wrapEl.createChild();
39053          
39054         this.footer = Roo.factory(this.footer, Roo);
39055         
39056     }
39057     */
39058     
39059      if(typeof config == "string"){
39060         this.title = config;
39061     }else{
39062         Roo.apply(this, config);
39063     }
39064     
39065     if(this.resizeEl){
39066         this.resizeEl = Roo.get(this.resizeEl, true);
39067     }else{
39068         this.resizeEl = this.el;
39069     }
39070     // handle view.xtype
39071     
39072  
39073     
39074     
39075     this.addEvents({
39076         /**
39077          * @event activate
39078          * Fires when this panel is activated. 
39079          * @param {Roo.ContentPanel} this
39080          */
39081         "activate" : true,
39082         /**
39083          * @event deactivate
39084          * Fires when this panel is activated. 
39085          * @param {Roo.ContentPanel} this
39086          */
39087         "deactivate" : true,
39088
39089         /**
39090          * @event resize
39091          * Fires when this panel is resized if fitToFrame is true.
39092          * @param {Roo.ContentPanel} this
39093          * @param {Number} width The width after any component adjustments
39094          * @param {Number} height The height after any component adjustments
39095          */
39096         "resize" : true,
39097         
39098          /**
39099          * @event render
39100          * Fires when this tab is created
39101          * @param {Roo.ContentPanel} this
39102          */
39103         "render" : true
39104         
39105         
39106         
39107     });
39108     
39109
39110     
39111     
39112     if(this.autoScroll){
39113         this.resizeEl.setStyle("overflow", "auto");
39114     } else {
39115         // fix randome scrolling
39116         //this.el.on('scroll', function() {
39117         //    Roo.log('fix random scolling');
39118         //    this.scrollTo('top',0); 
39119         //});
39120     }
39121     content = content || this.content;
39122     if(content){
39123         this.setContent(content);
39124     }
39125     if(config && config.url){
39126         this.setUrl(this.url, this.params, this.loadOnce);
39127     }
39128     
39129     
39130     
39131     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39132     
39133     if (this.view && typeof(this.view.xtype) != 'undefined') {
39134         this.view.el = this.el.appendChild(document.createElement("div"));
39135         this.view = Roo.factory(this.view); 
39136         this.view.render  &&  this.view.render(false, '');  
39137     }
39138     
39139     
39140     this.fireEvent('render', this);
39141 };
39142
39143 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39144     
39145     tabTip : '',
39146     
39147     setRegion : function(region){
39148         this.region = region;
39149         this.setActiveClass(region && !this.background);
39150     },
39151     
39152     
39153     setActiveClass: function(state)
39154     {
39155         if(state){
39156            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39157            this.el.setStyle('position','relative');
39158         }else{
39159            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39160            this.el.setStyle('position', 'absolute');
39161         } 
39162     },
39163     
39164     /**
39165      * Returns the toolbar for this Panel if one was configured. 
39166      * @return {Roo.Toolbar} 
39167      */
39168     getToolbar : function(){
39169         return this.toolbar;
39170     },
39171     
39172     setActiveState : function(active)
39173     {
39174         this.active = active;
39175         this.setActiveClass(active);
39176         if(!active){
39177             if(this.fireEvent("deactivate", this) === false){
39178                 return false;
39179             }
39180             return true;
39181         }
39182         this.fireEvent("activate", this);
39183         return true;
39184     },
39185     /**
39186      * Updates this panel's element
39187      * @param {String} content The new content
39188      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39189     */
39190     setContent : function(content, loadScripts){
39191         this.el.update(content, loadScripts);
39192     },
39193
39194     ignoreResize : function(w, h){
39195         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39196             return true;
39197         }else{
39198             this.lastSize = {width: w, height: h};
39199             return false;
39200         }
39201     },
39202     /**
39203      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39204      * @return {Roo.UpdateManager} The UpdateManager
39205      */
39206     getUpdateManager : function(){
39207         return this.el.getUpdateManager();
39208     },
39209      /**
39210      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39211      * @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:
39212 <pre><code>
39213 panel.load({
39214     url: "your-url.php",
39215     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39216     callback: yourFunction,
39217     scope: yourObject, //(optional scope)
39218     discardUrl: false,
39219     nocache: false,
39220     text: "Loading...",
39221     timeout: 30,
39222     scripts: false
39223 });
39224 </code></pre>
39225      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39226      * 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.
39227      * @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}
39228      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39229      * @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.
39230      * @return {Roo.ContentPanel} this
39231      */
39232     load : function(){
39233         var um = this.el.getUpdateManager();
39234         um.update.apply(um, arguments);
39235         return this;
39236     },
39237
39238
39239     /**
39240      * 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.
39241      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39242      * @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)
39243      * @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)
39244      * @return {Roo.UpdateManager} The UpdateManager
39245      */
39246     setUrl : function(url, params, loadOnce){
39247         if(this.refreshDelegate){
39248             this.removeListener("activate", this.refreshDelegate);
39249         }
39250         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39251         this.on("activate", this.refreshDelegate);
39252         return this.el.getUpdateManager();
39253     },
39254     
39255     _handleRefresh : function(url, params, loadOnce){
39256         if(!loadOnce || !this.loaded){
39257             var updater = this.el.getUpdateManager();
39258             updater.update(url, params, this._setLoaded.createDelegate(this));
39259         }
39260     },
39261     
39262     _setLoaded : function(){
39263         this.loaded = true;
39264     }, 
39265     
39266     /**
39267      * Returns this panel's id
39268      * @return {String} 
39269      */
39270     getId : function(){
39271         return this.el.id;
39272     },
39273     
39274     /** 
39275      * Returns this panel's element - used by regiosn to add.
39276      * @return {Roo.Element} 
39277      */
39278     getEl : function(){
39279         return this.wrapEl || this.el;
39280     },
39281     
39282    
39283     
39284     adjustForComponents : function(width, height)
39285     {
39286         //Roo.log('adjustForComponents ');
39287         if(this.resizeEl != this.el){
39288             width -= this.el.getFrameWidth('lr');
39289             height -= this.el.getFrameWidth('tb');
39290         }
39291         if(this.toolbar){
39292             var te = this.toolbar.getEl();
39293             te.setWidth(width);
39294             height -= te.getHeight();
39295         }
39296         if(this.footer){
39297             var te = this.footer.getEl();
39298             te.setWidth(width);
39299             height -= te.getHeight();
39300         }
39301         
39302         
39303         if(this.adjustments){
39304             width += this.adjustments[0];
39305             height += this.adjustments[1];
39306         }
39307         return {"width": width, "height": height};
39308     },
39309     
39310     setSize : function(width, height){
39311         if(this.fitToFrame && !this.ignoreResize(width, height)){
39312             if(this.fitContainer && this.resizeEl != this.el){
39313                 this.el.setSize(width, height);
39314             }
39315             var size = this.adjustForComponents(width, height);
39316             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39317             this.fireEvent('resize', this, size.width, size.height);
39318         }
39319     },
39320     
39321     /**
39322      * Returns this panel's title
39323      * @return {String} 
39324      */
39325     getTitle : function(){
39326         
39327         if (typeof(this.title) != 'object') {
39328             return this.title;
39329         }
39330         
39331         var t = '';
39332         for (var k in this.title) {
39333             if (!this.title.hasOwnProperty(k)) {
39334                 continue;
39335             }
39336             
39337             if (k.indexOf('-') >= 0) {
39338                 var s = k.split('-');
39339                 for (var i = 0; i<s.length; i++) {
39340                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39341                 }
39342             } else {
39343                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39344             }
39345         }
39346         return t;
39347     },
39348     
39349     /**
39350      * Set this panel's title
39351      * @param {String} title
39352      */
39353     setTitle : function(title){
39354         this.title = title;
39355         if(this.region){
39356             this.region.updatePanelTitle(this, title);
39357         }
39358     },
39359     
39360     /**
39361      * Returns true is this panel was configured to be closable
39362      * @return {Boolean} 
39363      */
39364     isClosable : function(){
39365         return this.closable;
39366     },
39367     
39368     beforeSlide : function(){
39369         this.el.clip();
39370         this.resizeEl.clip();
39371     },
39372     
39373     afterSlide : function(){
39374         this.el.unclip();
39375         this.resizeEl.unclip();
39376     },
39377     
39378     /**
39379      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39380      *   Will fail silently if the {@link #setUrl} method has not been called.
39381      *   This does not activate the panel, just updates its content.
39382      */
39383     refresh : function(){
39384         if(this.refreshDelegate){
39385            this.loaded = false;
39386            this.refreshDelegate();
39387         }
39388     },
39389     
39390     /**
39391      * Destroys this panel
39392      */
39393     destroy : function(){
39394         this.el.removeAllListeners();
39395         var tempEl = document.createElement("span");
39396         tempEl.appendChild(this.el.dom);
39397         tempEl.innerHTML = "";
39398         this.el.remove();
39399         this.el = null;
39400     },
39401     
39402     /**
39403      * form - if the content panel contains a form - this is a reference to it.
39404      * @type {Roo.form.Form}
39405      */
39406     form : false,
39407     /**
39408      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39409      *    This contains a reference to it.
39410      * @type {Roo.View}
39411      */
39412     view : false,
39413     
39414       /**
39415      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39416      * <pre><code>
39417
39418 layout.addxtype({
39419        xtype : 'Form',
39420        items: [ .... ]
39421    }
39422 );
39423
39424 </code></pre>
39425      * @param {Object} cfg Xtype definition of item to add.
39426      */
39427     
39428     
39429     getChildContainer: function () {
39430         return this.getEl();
39431     }
39432     
39433     
39434     /*
39435         var  ret = new Roo.factory(cfg);
39436         return ret;
39437         
39438         
39439         // add form..
39440         if (cfg.xtype.match(/^Form$/)) {
39441             
39442             var el;
39443             //if (this.footer) {
39444             //    el = this.footer.container.insertSibling(false, 'before');
39445             //} else {
39446                 el = this.el.createChild();
39447             //}
39448
39449             this.form = new  Roo.form.Form(cfg);
39450             
39451             
39452             if ( this.form.allItems.length) {
39453                 this.form.render(el.dom);
39454             }
39455             return this.form;
39456         }
39457         // should only have one of theses..
39458         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39459             // views.. should not be just added - used named prop 'view''
39460             
39461             cfg.el = this.el.appendChild(document.createElement("div"));
39462             // factory?
39463             
39464             var ret = new Roo.factory(cfg);
39465              
39466              ret.render && ret.render(false, ''); // render blank..
39467             this.view = ret;
39468             return ret;
39469         }
39470         return false;
39471     }
39472     \*/
39473 });
39474  
39475 /**
39476  * @class Roo.bootstrap.panel.Grid
39477  * @extends Roo.bootstrap.panel.Content
39478  * @constructor
39479  * Create a new GridPanel.
39480  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39481  * @param {Object} config A the config object
39482   
39483  */
39484
39485
39486
39487 Roo.bootstrap.panel.Grid = function(config)
39488 {
39489     
39490       
39491     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39492         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39493
39494     config.el = this.wrapper;
39495     //this.el = this.wrapper;
39496     
39497       if (config.container) {
39498         // ctor'ed from a Border/panel.grid
39499         
39500         
39501         this.wrapper.setStyle("overflow", "hidden");
39502         this.wrapper.addClass('roo-grid-container');
39503
39504     }
39505     
39506     
39507     if(config.toolbar){
39508         var tool_el = this.wrapper.createChild();    
39509         this.toolbar = Roo.factory(config.toolbar);
39510         var ti = [];
39511         if (config.toolbar.items) {
39512             ti = config.toolbar.items ;
39513             delete config.toolbar.items ;
39514         }
39515         
39516         var nitems = [];
39517         this.toolbar.render(tool_el);
39518         for(var i =0;i < ti.length;i++) {
39519           //  Roo.log(['add child', items[i]]);
39520             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39521         }
39522         this.toolbar.items = nitems;
39523         
39524         delete config.toolbar;
39525     }
39526     
39527     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39528     config.grid.scrollBody = true;;
39529     config.grid.monitorWindowResize = false; // turn off autosizing
39530     config.grid.autoHeight = false;
39531     config.grid.autoWidth = false;
39532     
39533     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39534     
39535     if (config.background) {
39536         // render grid on panel activation (if panel background)
39537         this.on('activate', function(gp) {
39538             if (!gp.grid.rendered) {
39539                 gp.grid.render(this.wrapper);
39540                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39541             }
39542         });
39543             
39544     } else {
39545         this.grid.render(this.wrapper);
39546         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39547
39548     }
39549     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39550     // ??? needed ??? config.el = this.wrapper;
39551     
39552     
39553     
39554   
39555     // xtype created footer. - not sure if will work as we normally have to render first..
39556     if (this.footer && !this.footer.el && this.footer.xtype) {
39557         
39558         var ctr = this.grid.getView().getFooterPanel(true);
39559         this.footer.dataSource = this.grid.dataSource;
39560         this.footer = Roo.factory(this.footer, Roo);
39561         this.footer.render(ctr);
39562         
39563     }
39564     
39565     
39566     
39567     
39568      
39569 };
39570
39571 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39572     getId : function(){
39573         return this.grid.id;
39574     },
39575     
39576     /**
39577      * Returns the grid for this panel
39578      * @return {Roo.bootstrap.Table} 
39579      */
39580     getGrid : function(){
39581         return this.grid;    
39582     },
39583     
39584     setSize : function(width, height){
39585         if(!this.ignoreResize(width, height)){
39586             var grid = this.grid;
39587             var size = this.adjustForComponents(width, height);
39588             // tfoot is not a footer?
39589           
39590             
39591             var gridel = grid.getGridEl();
39592             gridel.setSize(size.width, size.height);
39593             
39594             var tbd = grid.getGridEl().select('tbody', true).first();
39595             var thd = grid.getGridEl().select('thead',true).first();
39596             var tbf= grid.getGridEl().select('tfoot', true).first();
39597
39598             if (tbf) {
39599                 size.height -= thd.getHeight();
39600             }
39601             if (thd) {
39602                 size.height -= thd.getHeight();
39603             }
39604             
39605             tbd.setSize(size.width, size.height );
39606             // this is for the account management tab -seems to work there.
39607             var thd = grid.getGridEl().select('thead',true).first();
39608             //if (tbd) {
39609             //    tbd.setSize(size.width, size.height - thd.getHeight());
39610             //}
39611              
39612             grid.autoSize();
39613         }
39614     },
39615      
39616     
39617     
39618     beforeSlide : function(){
39619         this.grid.getView().scroller.clip();
39620     },
39621     
39622     afterSlide : function(){
39623         this.grid.getView().scroller.unclip();
39624     },
39625     
39626     destroy : function(){
39627         this.grid.destroy();
39628         delete this.grid;
39629         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39630     }
39631 });
39632
39633 /**
39634  * @class Roo.bootstrap.panel.Nest
39635  * @extends Roo.bootstrap.panel.Content
39636  * @constructor
39637  * Create a new Panel, that can contain a layout.Border.
39638  * 
39639  * 
39640  * @param {Roo.BorderLayout} layout The layout for this panel
39641  * @param {String/Object} config A string to set only the title or a config object
39642  */
39643 Roo.bootstrap.panel.Nest = function(config)
39644 {
39645     // construct with only one argument..
39646     /* FIXME - implement nicer consturctors
39647     if (layout.layout) {
39648         config = layout;
39649         layout = config.layout;
39650         delete config.layout;
39651     }
39652     if (layout.xtype && !layout.getEl) {
39653         // then layout needs constructing..
39654         layout = Roo.factory(layout, Roo);
39655     }
39656     */
39657     
39658     config.el =  config.layout.getEl();
39659     
39660     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39661     
39662     config.layout.monitorWindowResize = false; // turn off autosizing
39663     this.layout = config.layout;
39664     this.layout.getEl().addClass("roo-layout-nested-layout");
39665     this.layout.parent = this;
39666     
39667     
39668     
39669     
39670 };
39671
39672 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39673
39674     setSize : function(width, height){
39675         if(!this.ignoreResize(width, height)){
39676             var size = this.adjustForComponents(width, height);
39677             var el = this.layout.getEl();
39678             if (size.height < 1) {
39679                 el.setWidth(size.width);   
39680             } else {
39681                 el.setSize(size.width, size.height);
39682             }
39683             var touch = el.dom.offsetWidth;
39684             this.layout.layout();
39685             // ie requires a double layout on the first pass
39686             if(Roo.isIE && !this.initialized){
39687                 this.initialized = true;
39688                 this.layout.layout();
39689             }
39690         }
39691     },
39692     
39693     // activate all subpanels if not currently active..
39694     
39695     setActiveState : function(active){
39696         this.active = active;
39697         this.setActiveClass(active);
39698         
39699         if(!active){
39700             this.fireEvent("deactivate", this);
39701             return;
39702         }
39703         
39704         this.fireEvent("activate", this);
39705         // not sure if this should happen before or after..
39706         if (!this.layout) {
39707             return; // should not happen..
39708         }
39709         var reg = false;
39710         for (var r in this.layout.regions) {
39711             reg = this.layout.getRegion(r);
39712             if (reg.getActivePanel()) {
39713                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39714                 reg.setActivePanel(reg.getActivePanel());
39715                 continue;
39716             }
39717             if (!reg.panels.length) {
39718                 continue;
39719             }
39720             reg.showPanel(reg.getPanel(0));
39721         }
39722         
39723         
39724         
39725         
39726     },
39727     
39728     /**
39729      * Returns the nested BorderLayout for this panel
39730      * @return {Roo.BorderLayout} 
39731      */
39732     getLayout : function(){
39733         return this.layout;
39734     },
39735     
39736      /**
39737      * Adds a xtype elements to the layout of the nested panel
39738      * <pre><code>
39739
39740 panel.addxtype({
39741        xtype : 'ContentPanel',
39742        region: 'west',
39743        items: [ .... ]
39744    }
39745 );
39746
39747 panel.addxtype({
39748         xtype : 'NestedLayoutPanel',
39749         region: 'west',
39750         layout: {
39751            center: { },
39752            west: { }   
39753         },
39754         items : [ ... list of content panels or nested layout panels.. ]
39755    }
39756 );
39757 </code></pre>
39758      * @param {Object} cfg Xtype definition of item to add.
39759      */
39760     addxtype : function(cfg) {
39761         return this.layout.addxtype(cfg);
39762     
39763     }
39764 });/*
39765  * Based on:
39766  * Ext JS Library 1.1.1
39767  * Copyright(c) 2006-2007, Ext JS, LLC.
39768  *
39769  * Originally Released Under LGPL - original licence link has changed is not relivant.
39770  *
39771  * Fork - LGPL
39772  * <script type="text/javascript">
39773  */
39774 /**
39775  * @class Roo.TabPanel
39776  * @extends Roo.util.Observable
39777  * A lightweight tab container.
39778  * <br><br>
39779  * Usage:
39780  * <pre><code>
39781 // basic tabs 1, built from existing content
39782 var tabs = new Roo.TabPanel("tabs1");
39783 tabs.addTab("script", "View Script");
39784 tabs.addTab("markup", "View Markup");
39785 tabs.activate("script");
39786
39787 // more advanced tabs, built from javascript
39788 var jtabs = new Roo.TabPanel("jtabs");
39789 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39790
39791 // set up the UpdateManager
39792 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39793 var updater = tab2.getUpdateManager();
39794 updater.setDefaultUrl("ajax1.htm");
39795 tab2.on('activate', updater.refresh, updater, true);
39796
39797 // Use setUrl for Ajax loading
39798 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39799 tab3.setUrl("ajax2.htm", null, true);
39800
39801 // Disabled tab
39802 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39803 tab4.disable();
39804
39805 jtabs.activate("jtabs-1");
39806  * </code></pre>
39807  * @constructor
39808  * Create a new TabPanel.
39809  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39810  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39811  */
39812 Roo.bootstrap.panel.Tabs = function(config){
39813     /**
39814     * The container element for this TabPanel.
39815     * @type Roo.Element
39816     */
39817     this.el = Roo.get(config.el);
39818     delete config.el;
39819     if(config){
39820         if(typeof config == "boolean"){
39821             this.tabPosition = config ? "bottom" : "top";
39822         }else{
39823             Roo.apply(this, config);
39824         }
39825     }
39826     
39827     if(this.tabPosition == "bottom"){
39828         // if tabs are at the bottom = create the body first.
39829         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39830         this.el.addClass("roo-tabs-bottom");
39831     }
39832     // next create the tabs holders
39833     
39834     if (this.tabPosition == "west"){
39835         
39836         var reg = this.region; // fake it..
39837         while (reg) {
39838             if (!reg.mgr.parent) {
39839                 break;
39840             }
39841             reg = reg.mgr.parent.region;
39842         }
39843         Roo.log("got nest?");
39844         Roo.log(reg);
39845         if (reg.mgr.getRegion('west')) {
39846             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39847             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39848             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39849             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39850             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39851         
39852             
39853         }
39854         
39855         
39856     } else {
39857      
39858         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39859         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39860         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39861         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39862     }
39863     
39864     
39865     if(Roo.isIE){
39866         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39867     }
39868     
39869     // finally - if tabs are at the top, then create the body last..
39870     if(this.tabPosition != "bottom"){
39871         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39872          * @type Roo.Element
39873          */
39874         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39875         this.el.addClass("roo-tabs-top");
39876     }
39877     this.items = [];
39878
39879     this.bodyEl.setStyle("position", "relative");
39880
39881     this.active = null;
39882     this.activateDelegate = this.activate.createDelegate(this);
39883
39884     this.addEvents({
39885         /**
39886          * @event tabchange
39887          * Fires when the active tab changes
39888          * @param {Roo.TabPanel} this
39889          * @param {Roo.TabPanelItem} activePanel The new active tab
39890          */
39891         "tabchange": true,
39892         /**
39893          * @event beforetabchange
39894          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39895          * @param {Roo.TabPanel} this
39896          * @param {Object} e Set cancel to true on this object to cancel the tab change
39897          * @param {Roo.TabPanelItem} tab The tab being changed to
39898          */
39899         "beforetabchange" : true
39900     });
39901
39902     Roo.EventManager.onWindowResize(this.onResize, this);
39903     this.cpad = this.el.getPadding("lr");
39904     this.hiddenCount = 0;
39905
39906
39907     // toolbar on the tabbar support...
39908     if (this.toolbar) {
39909         alert("no toolbar support yet");
39910         this.toolbar  = false;
39911         /*
39912         var tcfg = this.toolbar;
39913         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39914         this.toolbar = new Roo.Toolbar(tcfg);
39915         if (Roo.isSafari) {
39916             var tbl = tcfg.container.child('table', true);
39917             tbl.setAttribute('width', '100%');
39918         }
39919         */
39920         
39921     }
39922    
39923
39924
39925     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39926 };
39927
39928 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39929     /*
39930      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39931      */
39932     tabPosition : "top",
39933     /*
39934      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39935      */
39936     currentTabWidth : 0,
39937     /*
39938      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39939      */
39940     minTabWidth : 40,
39941     /*
39942      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39943      */
39944     maxTabWidth : 250,
39945     /*
39946      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39947      */
39948     preferredTabWidth : 175,
39949     /*
39950      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39951      */
39952     resizeTabs : false,
39953     /*
39954      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39955      */
39956     monitorResize : true,
39957     /*
39958      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39959      */
39960     toolbar : false,  // set by caller..
39961     
39962     region : false, /// set by caller
39963     
39964     disableTooltips : true, // not used yet...
39965
39966     /**
39967      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39968      * @param {String} id The id of the div to use <b>or create</b>
39969      * @param {String} text The text for the tab
39970      * @param {String} content (optional) Content to put in the TabPanelItem body
39971      * @param {Boolean} closable (optional) True to create a close icon on the tab
39972      * @return {Roo.TabPanelItem} The created TabPanelItem
39973      */
39974     addTab : function(id, text, content, closable, tpl)
39975     {
39976         var item = new Roo.bootstrap.panel.TabItem({
39977             panel: this,
39978             id : id,
39979             text : text,
39980             closable : closable,
39981             tpl : tpl
39982         });
39983         this.addTabItem(item);
39984         if(content){
39985             item.setContent(content);
39986         }
39987         return item;
39988     },
39989
39990     /**
39991      * Returns the {@link Roo.TabPanelItem} with the specified id/index
39992      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
39993      * @return {Roo.TabPanelItem}
39994      */
39995     getTab : function(id){
39996         return this.items[id];
39997     },
39998
39999     /**
40000      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40001      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40002      */
40003     hideTab : function(id){
40004         var t = this.items[id];
40005         if(!t.isHidden()){
40006            t.setHidden(true);
40007            this.hiddenCount++;
40008            this.autoSizeTabs();
40009         }
40010     },
40011
40012     /**
40013      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40014      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40015      */
40016     unhideTab : function(id){
40017         var t = this.items[id];
40018         if(t.isHidden()){
40019            t.setHidden(false);
40020            this.hiddenCount--;
40021            this.autoSizeTabs();
40022         }
40023     },
40024
40025     /**
40026      * Adds an existing {@link Roo.TabPanelItem}.
40027      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40028      */
40029     addTabItem : function(item)
40030     {
40031         this.items[item.id] = item;
40032         this.items.push(item);
40033         this.autoSizeTabs();
40034       //  if(this.resizeTabs){
40035     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40036   //         this.autoSizeTabs();
40037 //        }else{
40038 //            item.autoSize();
40039        // }
40040     },
40041
40042     /**
40043      * Removes a {@link Roo.TabPanelItem}.
40044      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40045      */
40046     removeTab : function(id){
40047         var items = this.items;
40048         var tab = items[id];
40049         if(!tab) { return; }
40050         var index = items.indexOf(tab);
40051         if(this.active == tab && items.length > 1){
40052             var newTab = this.getNextAvailable(index);
40053             if(newTab) {
40054                 newTab.activate();
40055             }
40056         }
40057         this.stripEl.dom.removeChild(tab.pnode.dom);
40058         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40059             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40060         }
40061         items.splice(index, 1);
40062         delete this.items[tab.id];
40063         tab.fireEvent("close", tab);
40064         tab.purgeListeners();
40065         this.autoSizeTabs();
40066     },
40067
40068     getNextAvailable : function(start){
40069         var items = this.items;
40070         var index = start;
40071         // look for a next tab that will slide over to
40072         // replace the one being removed
40073         while(index < items.length){
40074             var item = items[++index];
40075             if(item && !item.isHidden()){
40076                 return item;
40077             }
40078         }
40079         // if one isn't found select the previous tab (on the left)
40080         index = start;
40081         while(index >= 0){
40082             var item = items[--index];
40083             if(item && !item.isHidden()){
40084                 return item;
40085             }
40086         }
40087         return null;
40088     },
40089
40090     /**
40091      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40092      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40093      */
40094     disableTab : function(id){
40095         var tab = this.items[id];
40096         if(tab && this.active != tab){
40097             tab.disable();
40098         }
40099     },
40100
40101     /**
40102      * Enables a {@link Roo.TabPanelItem} that is disabled.
40103      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40104      */
40105     enableTab : function(id){
40106         var tab = this.items[id];
40107         tab.enable();
40108     },
40109
40110     /**
40111      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40112      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40113      * @return {Roo.TabPanelItem} The TabPanelItem.
40114      */
40115     activate : function(id)
40116     {
40117         //Roo.log('activite:'  + id);
40118         
40119         var tab = this.items[id];
40120         if(!tab){
40121             return null;
40122         }
40123         if(tab == this.active || tab.disabled){
40124             return tab;
40125         }
40126         var e = {};
40127         this.fireEvent("beforetabchange", this, e, tab);
40128         if(e.cancel !== true && !tab.disabled){
40129             if(this.active){
40130                 this.active.hide();
40131             }
40132             this.active = this.items[id];
40133             this.active.show();
40134             this.fireEvent("tabchange", this, this.active);
40135         }
40136         return tab;
40137     },
40138
40139     /**
40140      * Gets the active {@link Roo.TabPanelItem}.
40141      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40142      */
40143     getActiveTab : function(){
40144         return this.active;
40145     },
40146
40147     /**
40148      * Updates the tab body element to fit the height of the container element
40149      * for overflow scrolling
40150      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40151      */
40152     syncHeight : function(targetHeight){
40153         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40154         var bm = this.bodyEl.getMargins();
40155         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40156         this.bodyEl.setHeight(newHeight);
40157         return newHeight;
40158     },
40159
40160     onResize : function(){
40161         if(this.monitorResize){
40162             this.autoSizeTabs();
40163         }
40164     },
40165
40166     /**
40167      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40168      */
40169     beginUpdate : function(){
40170         this.updating = true;
40171     },
40172
40173     /**
40174      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40175      */
40176     endUpdate : function(){
40177         this.updating = false;
40178         this.autoSizeTabs();
40179     },
40180
40181     /**
40182      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40183      */
40184     autoSizeTabs : function()
40185     {
40186         var count = this.items.length;
40187         var vcount = count - this.hiddenCount;
40188         
40189         if (vcount < 2) {
40190             this.stripEl.hide();
40191         } else {
40192             this.stripEl.show();
40193         }
40194         
40195         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40196             return;
40197         }
40198         
40199         
40200         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40201         var availWidth = Math.floor(w / vcount);
40202         var b = this.stripBody;
40203         if(b.getWidth() > w){
40204             var tabs = this.items;
40205             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40206             if(availWidth < this.minTabWidth){
40207                 /*if(!this.sleft){    // incomplete scrolling code
40208                     this.createScrollButtons();
40209                 }
40210                 this.showScroll();
40211                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40212             }
40213         }else{
40214             if(this.currentTabWidth < this.preferredTabWidth){
40215                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40216             }
40217         }
40218     },
40219
40220     /**
40221      * Returns the number of tabs in this TabPanel.
40222      * @return {Number}
40223      */
40224      getCount : function(){
40225          return this.items.length;
40226      },
40227
40228     /**
40229      * Resizes all the tabs to the passed width
40230      * @param {Number} The new width
40231      */
40232     setTabWidth : function(width){
40233         this.currentTabWidth = width;
40234         for(var i = 0, len = this.items.length; i < len; i++) {
40235                 if(!this.items[i].isHidden()) {
40236                 this.items[i].setWidth(width);
40237             }
40238         }
40239     },
40240
40241     /**
40242      * Destroys this TabPanel
40243      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40244      */
40245     destroy : function(removeEl){
40246         Roo.EventManager.removeResizeListener(this.onResize, this);
40247         for(var i = 0, len = this.items.length; i < len; i++){
40248             this.items[i].purgeListeners();
40249         }
40250         if(removeEl === true){
40251             this.el.update("");
40252             this.el.remove();
40253         }
40254     },
40255     
40256     createStrip : function(container)
40257     {
40258         var strip = document.createElement("nav");
40259         strip.className = Roo.bootstrap.version == 4 ?
40260             "navbar-light bg-light" : 
40261             "navbar navbar-default"; //"x-tabs-wrap";
40262         container.appendChild(strip);
40263         return strip;
40264     },
40265     
40266     createStripList : function(strip)
40267     {
40268         // div wrapper for retard IE
40269         // returns the "tr" element.
40270         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40271         //'<div class="x-tabs-strip-wrap">'+
40272           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40273           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40274         return strip.firstChild; //.firstChild.firstChild.firstChild;
40275     },
40276     createBody : function(container)
40277     {
40278         var body = document.createElement("div");
40279         Roo.id(body, "tab-body");
40280         //Roo.fly(body).addClass("x-tabs-body");
40281         Roo.fly(body).addClass("tab-content");
40282         container.appendChild(body);
40283         return body;
40284     },
40285     createItemBody :function(bodyEl, id){
40286         var body = Roo.getDom(id);
40287         if(!body){
40288             body = document.createElement("div");
40289             body.id = id;
40290         }
40291         //Roo.fly(body).addClass("x-tabs-item-body");
40292         Roo.fly(body).addClass("tab-pane");
40293          bodyEl.insertBefore(body, bodyEl.firstChild);
40294         return body;
40295     },
40296     /** @private */
40297     createStripElements :  function(stripEl, text, closable, tpl)
40298     {
40299         var td = document.createElement("li"); // was td..
40300         td.className = 'nav-item';
40301         
40302         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40303         
40304         
40305         stripEl.appendChild(td);
40306         /*if(closable){
40307             td.className = "x-tabs-closable";
40308             if(!this.closeTpl){
40309                 this.closeTpl = new Roo.Template(
40310                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40311                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40312                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40313                 );
40314             }
40315             var el = this.closeTpl.overwrite(td, {"text": text});
40316             var close = el.getElementsByTagName("div")[0];
40317             var inner = el.getElementsByTagName("em")[0];
40318             return {"el": el, "close": close, "inner": inner};
40319         } else {
40320         */
40321         // not sure what this is..
40322 //            if(!this.tabTpl){
40323                 //this.tabTpl = new Roo.Template(
40324                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40325                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40326                 //);
40327 //                this.tabTpl = new Roo.Template(
40328 //                   '<a href="#">' +
40329 //                   '<span unselectable="on"' +
40330 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40331 //                            ' >{text}</span></a>'
40332 //                );
40333 //                
40334 //            }
40335
40336
40337             var template = tpl || this.tabTpl || false;
40338             
40339             if(!template){
40340                 template =  new Roo.Template(
40341                         Roo.bootstrap.version == 4 ? 
40342                             (
40343                                 '<a class="nav-link" href="#" unselectable="on"' +
40344                                      (this.disableTooltips ? '' : ' title="{text}"') +
40345                                      ' >{text}</a>'
40346                             ) : (
40347                                 '<a class="nav-link" href="#">' +
40348                                 '<span unselectable="on"' +
40349                                          (this.disableTooltips ? '' : ' title="{text}"') +
40350                                     ' >{text}</span></a>'
40351                             )
40352                 );
40353             }
40354             
40355             switch (typeof(template)) {
40356                 case 'object' :
40357                     break;
40358                 case 'string' :
40359                     template = new Roo.Template(template);
40360                     break;
40361                 default :
40362                     break;
40363             }
40364             
40365             var el = template.overwrite(td, {"text": text});
40366             
40367             var inner = el.getElementsByTagName("span")[0];
40368             
40369             return {"el": el, "inner": inner};
40370             
40371     }
40372         
40373     
40374 });
40375
40376 /**
40377  * @class Roo.TabPanelItem
40378  * @extends Roo.util.Observable
40379  * Represents an individual item (tab plus body) in a TabPanel.
40380  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40381  * @param {String} id The id of this TabPanelItem
40382  * @param {String} text The text for the tab of this TabPanelItem
40383  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40384  */
40385 Roo.bootstrap.panel.TabItem = function(config){
40386     /**
40387      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40388      * @type Roo.TabPanel
40389      */
40390     this.tabPanel = config.panel;
40391     /**
40392      * The id for this TabPanelItem
40393      * @type String
40394      */
40395     this.id = config.id;
40396     /** @private */
40397     this.disabled = false;
40398     /** @private */
40399     this.text = config.text;
40400     /** @private */
40401     this.loaded = false;
40402     this.closable = config.closable;
40403
40404     /**
40405      * The body element for this TabPanelItem.
40406      * @type Roo.Element
40407      */
40408     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40409     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40410     this.bodyEl.setStyle("display", "block");
40411     this.bodyEl.setStyle("zoom", "1");
40412     //this.hideAction();
40413
40414     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40415     /** @private */
40416     this.el = Roo.get(els.el);
40417     this.inner = Roo.get(els.inner, true);
40418      this.textEl = Roo.bootstrap.version == 4 ?
40419         this.el : Roo.get(this.el.dom.firstChild, true);
40420
40421     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40422     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40423
40424     
40425 //    this.el.on("mousedown", this.onTabMouseDown, this);
40426     this.el.on("click", this.onTabClick, this);
40427     /** @private */
40428     if(config.closable){
40429         var c = Roo.get(els.close, true);
40430         c.dom.title = this.closeText;
40431         c.addClassOnOver("close-over");
40432         c.on("click", this.closeClick, this);
40433      }
40434
40435     this.addEvents({
40436          /**
40437          * @event activate
40438          * Fires when this tab becomes the active tab.
40439          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40440          * @param {Roo.TabPanelItem} this
40441          */
40442         "activate": true,
40443         /**
40444          * @event beforeclose
40445          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40446          * @param {Roo.TabPanelItem} this
40447          * @param {Object} e Set cancel to true on this object to cancel the close.
40448          */
40449         "beforeclose": true,
40450         /**
40451          * @event close
40452          * Fires when this tab is closed.
40453          * @param {Roo.TabPanelItem} this
40454          */
40455          "close": true,
40456         /**
40457          * @event deactivate
40458          * Fires when this tab is no longer the active tab.
40459          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40460          * @param {Roo.TabPanelItem} this
40461          */
40462          "deactivate" : true
40463     });
40464     this.hidden = false;
40465
40466     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40467 };
40468
40469 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40470            {
40471     purgeListeners : function(){
40472        Roo.util.Observable.prototype.purgeListeners.call(this);
40473        this.el.removeAllListeners();
40474     },
40475     /**
40476      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40477      */
40478     show : function(){
40479         this.status_node.addClass("active");
40480         this.showAction();
40481         if(Roo.isOpera){
40482             this.tabPanel.stripWrap.repaint();
40483         }
40484         this.fireEvent("activate", this.tabPanel, this);
40485     },
40486
40487     /**
40488      * Returns true if this tab is the active tab.
40489      * @return {Boolean}
40490      */
40491     isActive : function(){
40492         return this.tabPanel.getActiveTab() == this;
40493     },
40494
40495     /**
40496      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40497      */
40498     hide : function(){
40499         this.status_node.removeClass("active");
40500         this.hideAction();
40501         this.fireEvent("deactivate", this.tabPanel, this);
40502     },
40503
40504     hideAction : function(){
40505         this.bodyEl.hide();
40506         this.bodyEl.setStyle("position", "absolute");
40507         this.bodyEl.setLeft("-20000px");
40508         this.bodyEl.setTop("-20000px");
40509     },
40510
40511     showAction : function(){
40512         this.bodyEl.setStyle("position", "relative");
40513         this.bodyEl.setTop("");
40514         this.bodyEl.setLeft("");
40515         this.bodyEl.show();
40516     },
40517
40518     /**
40519      * Set the tooltip for the tab.
40520      * @param {String} tooltip The tab's tooltip
40521      */
40522     setTooltip : function(text){
40523         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40524             this.textEl.dom.qtip = text;
40525             this.textEl.dom.removeAttribute('title');
40526         }else{
40527             this.textEl.dom.title = text;
40528         }
40529     },
40530
40531     onTabClick : function(e){
40532         e.preventDefault();
40533         this.tabPanel.activate(this.id);
40534     },
40535
40536     onTabMouseDown : function(e){
40537         e.preventDefault();
40538         this.tabPanel.activate(this.id);
40539     },
40540 /*
40541     getWidth : function(){
40542         return this.inner.getWidth();
40543     },
40544
40545     setWidth : function(width){
40546         var iwidth = width - this.linode.getPadding("lr");
40547         this.inner.setWidth(iwidth);
40548         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40549         this.linode.setWidth(width);
40550     },
40551 */
40552     /**
40553      * Show or hide the tab
40554      * @param {Boolean} hidden True to hide or false to show.
40555      */
40556     setHidden : function(hidden){
40557         this.hidden = hidden;
40558         this.linode.setStyle("display", hidden ? "none" : "");
40559     },
40560
40561     /**
40562      * Returns true if this tab is "hidden"
40563      * @return {Boolean}
40564      */
40565     isHidden : function(){
40566         return this.hidden;
40567     },
40568
40569     /**
40570      * Returns the text for this tab
40571      * @return {String}
40572      */
40573     getText : function(){
40574         return this.text;
40575     },
40576     /*
40577     autoSize : function(){
40578         //this.el.beginMeasure();
40579         this.textEl.setWidth(1);
40580         /*
40581          *  #2804 [new] Tabs in Roojs
40582          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40583          */
40584         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40585         //this.el.endMeasure();
40586     //},
40587
40588     /**
40589      * Sets the text for the tab (Note: this also sets the tooltip text)
40590      * @param {String} text The tab's text and tooltip
40591      */
40592     setText : function(text){
40593         this.text = text;
40594         this.textEl.update(text);
40595         this.setTooltip(text);
40596         //if(!this.tabPanel.resizeTabs){
40597         //    this.autoSize();
40598         //}
40599     },
40600     /**
40601      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40602      */
40603     activate : function(){
40604         this.tabPanel.activate(this.id);
40605     },
40606
40607     /**
40608      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40609      */
40610     disable : function(){
40611         if(this.tabPanel.active != this){
40612             this.disabled = true;
40613             this.status_node.addClass("disabled");
40614         }
40615     },
40616
40617     /**
40618      * Enables this TabPanelItem if it was previously disabled.
40619      */
40620     enable : function(){
40621         this.disabled = false;
40622         this.status_node.removeClass("disabled");
40623     },
40624
40625     /**
40626      * Sets the content for this TabPanelItem.
40627      * @param {String} content The content
40628      * @param {Boolean} loadScripts true to look for and load scripts
40629      */
40630     setContent : function(content, loadScripts){
40631         this.bodyEl.update(content, loadScripts);
40632     },
40633
40634     /**
40635      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40636      * @return {Roo.UpdateManager} The UpdateManager
40637      */
40638     getUpdateManager : function(){
40639         return this.bodyEl.getUpdateManager();
40640     },
40641
40642     /**
40643      * Set a URL to be used to load the content for this TabPanelItem.
40644      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40645      * @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)
40646      * @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)
40647      * @return {Roo.UpdateManager} The UpdateManager
40648      */
40649     setUrl : function(url, params, loadOnce){
40650         if(this.refreshDelegate){
40651             this.un('activate', this.refreshDelegate);
40652         }
40653         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40654         this.on("activate", this.refreshDelegate);
40655         return this.bodyEl.getUpdateManager();
40656     },
40657
40658     /** @private */
40659     _handleRefresh : function(url, params, loadOnce){
40660         if(!loadOnce || !this.loaded){
40661             var updater = this.bodyEl.getUpdateManager();
40662             updater.update(url, params, this._setLoaded.createDelegate(this));
40663         }
40664     },
40665
40666     /**
40667      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40668      *   Will fail silently if the setUrl method has not been called.
40669      *   This does not activate the panel, just updates its content.
40670      */
40671     refresh : function(){
40672         if(this.refreshDelegate){
40673            this.loaded = false;
40674            this.refreshDelegate();
40675         }
40676     },
40677
40678     /** @private */
40679     _setLoaded : function(){
40680         this.loaded = true;
40681     },
40682
40683     /** @private */
40684     closeClick : function(e){
40685         var o = {};
40686         e.stopEvent();
40687         this.fireEvent("beforeclose", this, o);
40688         if(o.cancel !== true){
40689             this.tabPanel.removeTab(this.id);
40690         }
40691     },
40692     /**
40693      * The text displayed in the tooltip for the close icon.
40694      * @type String
40695      */
40696     closeText : "Close this tab"
40697 });
40698 /**
40699 *    This script refer to:
40700 *    Title: International Telephone Input
40701 *    Author: Jack O'Connor
40702 *    Code version:  v12.1.12
40703 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40704 **/
40705
40706 Roo.bootstrap.PhoneInputData = function() {
40707     var d = [
40708       [
40709         "Afghanistan (‫افغانستان‬‎)",
40710         "af",
40711         "93"
40712       ],
40713       [
40714         "Albania (Shqipëri)",
40715         "al",
40716         "355"
40717       ],
40718       [
40719         "Algeria (‫الجزائر‬‎)",
40720         "dz",
40721         "213"
40722       ],
40723       [
40724         "American Samoa",
40725         "as",
40726         "1684"
40727       ],
40728       [
40729         "Andorra",
40730         "ad",
40731         "376"
40732       ],
40733       [
40734         "Angola",
40735         "ao",
40736         "244"
40737       ],
40738       [
40739         "Anguilla",
40740         "ai",
40741         "1264"
40742       ],
40743       [
40744         "Antigua and Barbuda",
40745         "ag",
40746         "1268"
40747       ],
40748       [
40749         "Argentina",
40750         "ar",
40751         "54"
40752       ],
40753       [
40754         "Armenia (Հայաստան)",
40755         "am",
40756         "374"
40757       ],
40758       [
40759         "Aruba",
40760         "aw",
40761         "297"
40762       ],
40763       [
40764         "Australia",
40765         "au",
40766         "61",
40767         0
40768       ],
40769       [
40770         "Austria (Österreich)",
40771         "at",
40772         "43"
40773       ],
40774       [
40775         "Azerbaijan (Azərbaycan)",
40776         "az",
40777         "994"
40778       ],
40779       [
40780         "Bahamas",
40781         "bs",
40782         "1242"
40783       ],
40784       [
40785         "Bahrain (‫البحرين‬‎)",
40786         "bh",
40787         "973"
40788       ],
40789       [
40790         "Bangladesh (বাংলাদেশ)",
40791         "bd",
40792         "880"
40793       ],
40794       [
40795         "Barbados",
40796         "bb",
40797         "1246"
40798       ],
40799       [
40800         "Belarus (Беларусь)",
40801         "by",
40802         "375"
40803       ],
40804       [
40805         "Belgium (België)",
40806         "be",
40807         "32"
40808       ],
40809       [
40810         "Belize",
40811         "bz",
40812         "501"
40813       ],
40814       [
40815         "Benin (Bénin)",
40816         "bj",
40817         "229"
40818       ],
40819       [
40820         "Bermuda",
40821         "bm",
40822         "1441"
40823       ],
40824       [
40825         "Bhutan (འབྲུག)",
40826         "bt",
40827         "975"
40828       ],
40829       [
40830         "Bolivia",
40831         "bo",
40832         "591"
40833       ],
40834       [
40835         "Bosnia and Herzegovina (Босна и Херцеговина)",
40836         "ba",
40837         "387"
40838       ],
40839       [
40840         "Botswana",
40841         "bw",
40842         "267"
40843       ],
40844       [
40845         "Brazil (Brasil)",
40846         "br",
40847         "55"
40848       ],
40849       [
40850         "British Indian Ocean Territory",
40851         "io",
40852         "246"
40853       ],
40854       [
40855         "British Virgin Islands",
40856         "vg",
40857         "1284"
40858       ],
40859       [
40860         "Brunei",
40861         "bn",
40862         "673"
40863       ],
40864       [
40865         "Bulgaria (България)",
40866         "bg",
40867         "359"
40868       ],
40869       [
40870         "Burkina Faso",
40871         "bf",
40872         "226"
40873       ],
40874       [
40875         "Burundi (Uburundi)",
40876         "bi",
40877         "257"
40878       ],
40879       [
40880         "Cambodia (កម្ពុជា)",
40881         "kh",
40882         "855"
40883       ],
40884       [
40885         "Cameroon (Cameroun)",
40886         "cm",
40887         "237"
40888       ],
40889       [
40890         "Canada",
40891         "ca",
40892         "1",
40893         1,
40894         ["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"]
40895       ],
40896       [
40897         "Cape Verde (Kabu Verdi)",
40898         "cv",
40899         "238"
40900       ],
40901       [
40902         "Caribbean Netherlands",
40903         "bq",
40904         "599",
40905         1
40906       ],
40907       [
40908         "Cayman Islands",
40909         "ky",
40910         "1345"
40911       ],
40912       [
40913         "Central African Republic (République centrafricaine)",
40914         "cf",
40915         "236"
40916       ],
40917       [
40918         "Chad (Tchad)",
40919         "td",
40920         "235"
40921       ],
40922       [
40923         "Chile",
40924         "cl",
40925         "56"
40926       ],
40927       [
40928         "China (中国)",
40929         "cn",
40930         "86"
40931       ],
40932       [
40933         "Christmas Island",
40934         "cx",
40935         "61",
40936         2
40937       ],
40938       [
40939         "Cocos (Keeling) Islands",
40940         "cc",
40941         "61",
40942         1
40943       ],
40944       [
40945         "Colombia",
40946         "co",
40947         "57"
40948       ],
40949       [
40950         "Comoros (‫جزر القمر‬‎)",
40951         "km",
40952         "269"
40953       ],
40954       [
40955         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40956         "cd",
40957         "243"
40958       ],
40959       [
40960         "Congo (Republic) (Congo-Brazzaville)",
40961         "cg",
40962         "242"
40963       ],
40964       [
40965         "Cook Islands",
40966         "ck",
40967         "682"
40968       ],
40969       [
40970         "Costa Rica",
40971         "cr",
40972         "506"
40973       ],
40974       [
40975         "Côte d’Ivoire",
40976         "ci",
40977         "225"
40978       ],
40979       [
40980         "Croatia (Hrvatska)",
40981         "hr",
40982         "385"
40983       ],
40984       [
40985         "Cuba",
40986         "cu",
40987         "53"
40988       ],
40989       [
40990         "Curaçao",
40991         "cw",
40992         "599",
40993         0
40994       ],
40995       [
40996         "Cyprus (Κύπρος)",
40997         "cy",
40998         "357"
40999       ],
41000       [
41001         "Czech Republic (Česká republika)",
41002         "cz",
41003         "420"
41004       ],
41005       [
41006         "Denmark (Danmark)",
41007         "dk",
41008         "45"
41009       ],
41010       [
41011         "Djibouti",
41012         "dj",
41013         "253"
41014       ],
41015       [
41016         "Dominica",
41017         "dm",
41018         "1767"
41019       ],
41020       [
41021         "Dominican Republic (República Dominicana)",
41022         "do",
41023         "1",
41024         2,
41025         ["809", "829", "849"]
41026       ],
41027       [
41028         "Ecuador",
41029         "ec",
41030         "593"
41031       ],
41032       [
41033         "Egypt (‫مصر‬‎)",
41034         "eg",
41035         "20"
41036       ],
41037       [
41038         "El Salvador",
41039         "sv",
41040         "503"
41041       ],
41042       [
41043         "Equatorial Guinea (Guinea Ecuatorial)",
41044         "gq",
41045         "240"
41046       ],
41047       [
41048         "Eritrea",
41049         "er",
41050         "291"
41051       ],
41052       [
41053         "Estonia (Eesti)",
41054         "ee",
41055         "372"
41056       ],
41057       [
41058         "Ethiopia",
41059         "et",
41060         "251"
41061       ],
41062       [
41063         "Falkland Islands (Islas Malvinas)",
41064         "fk",
41065         "500"
41066       ],
41067       [
41068         "Faroe Islands (Føroyar)",
41069         "fo",
41070         "298"
41071       ],
41072       [
41073         "Fiji",
41074         "fj",
41075         "679"
41076       ],
41077       [
41078         "Finland (Suomi)",
41079         "fi",
41080         "358",
41081         0
41082       ],
41083       [
41084         "France",
41085         "fr",
41086         "33"
41087       ],
41088       [
41089         "French Guiana (Guyane française)",
41090         "gf",
41091         "594"
41092       ],
41093       [
41094         "French Polynesia (Polynésie française)",
41095         "pf",
41096         "689"
41097       ],
41098       [
41099         "Gabon",
41100         "ga",
41101         "241"
41102       ],
41103       [
41104         "Gambia",
41105         "gm",
41106         "220"
41107       ],
41108       [
41109         "Georgia (საქართველო)",
41110         "ge",
41111         "995"
41112       ],
41113       [
41114         "Germany (Deutschland)",
41115         "de",
41116         "49"
41117       ],
41118       [
41119         "Ghana (Gaana)",
41120         "gh",
41121         "233"
41122       ],
41123       [
41124         "Gibraltar",
41125         "gi",
41126         "350"
41127       ],
41128       [
41129         "Greece (Ελλάδα)",
41130         "gr",
41131         "30"
41132       ],
41133       [
41134         "Greenland (Kalaallit Nunaat)",
41135         "gl",
41136         "299"
41137       ],
41138       [
41139         "Grenada",
41140         "gd",
41141         "1473"
41142       ],
41143       [
41144         "Guadeloupe",
41145         "gp",
41146         "590",
41147         0
41148       ],
41149       [
41150         "Guam",
41151         "gu",
41152         "1671"
41153       ],
41154       [
41155         "Guatemala",
41156         "gt",
41157         "502"
41158       ],
41159       [
41160         "Guernsey",
41161         "gg",
41162         "44",
41163         1
41164       ],
41165       [
41166         "Guinea (Guinée)",
41167         "gn",
41168         "224"
41169       ],
41170       [
41171         "Guinea-Bissau (Guiné Bissau)",
41172         "gw",
41173         "245"
41174       ],
41175       [
41176         "Guyana",
41177         "gy",
41178         "592"
41179       ],
41180       [
41181         "Haiti",
41182         "ht",
41183         "509"
41184       ],
41185       [
41186         "Honduras",
41187         "hn",
41188         "504"
41189       ],
41190       [
41191         "Hong Kong (香港)",
41192         "hk",
41193         "852"
41194       ],
41195       [
41196         "Hungary (Magyarország)",
41197         "hu",
41198         "36"
41199       ],
41200       [
41201         "Iceland (Ísland)",
41202         "is",
41203         "354"
41204       ],
41205       [
41206         "India (भारत)",
41207         "in",
41208         "91"
41209       ],
41210       [
41211         "Indonesia",
41212         "id",
41213         "62"
41214       ],
41215       [
41216         "Iran (‫ایران‬‎)",
41217         "ir",
41218         "98"
41219       ],
41220       [
41221         "Iraq (‫العراق‬‎)",
41222         "iq",
41223         "964"
41224       ],
41225       [
41226         "Ireland",
41227         "ie",
41228         "353"
41229       ],
41230       [
41231         "Isle of Man",
41232         "im",
41233         "44",
41234         2
41235       ],
41236       [
41237         "Israel (‫ישראל‬‎)",
41238         "il",
41239         "972"
41240       ],
41241       [
41242         "Italy (Italia)",
41243         "it",
41244         "39",
41245         0
41246       ],
41247       [
41248         "Jamaica",
41249         "jm",
41250         "1876"
41251       ],
41252       [
41253         "Japan (日本)",
41254         "jp",
41255         "81"
41256       ],
41257       [
41258         "Jersey",
41259         "je",
41260         "44",
41261         3
41262       ],
41263       [
41264         "Jordan (‫الأردن‬‎)",
41265         "jo",
41266         "962"
41267       ],
41268       [
41269         "Kazakhstan (Казахстан)",
41270         "kz",
41271         "7",
41272         1
41273       ],
41274       [
41275         "Kenya",
41276         "ke",
41277         "254"
41278       ],
41279       [
41280         "Kiribati",
41281         "ki",
41282         "686"
41283       ],
41284       [
41285         "Kosovo",
41286         "xk",
41287         "383"
41288       ],
41289       [
41290         "Kuwait (‫الكويت‬‎)",
41291         "kw",
41292         "965"
41293       ],
41294       [
41295         "Kyrgyzstan (Кыргызстан)",
41296         "kg",
41297         "996"
41298       ],
41299       [
41300         "Laos (ລາວ)",
41301         "la",
41302         "856"
41303       ],
41304       [
41305         "Latvia (Latvija)",
41306         "lv",
41307         "371"
41308       ],
41309       [
41310         "Lebanon (‫لبنان‬‎)",
41311         "lb",
41312         "961"
41313       ],
41314       [
41315         "Lesotho",
41316         "ls",
41317         "266"
41318       ],
41319       [
41320         "Liberia",
41321         "lr",
41322         "231"
41323       ],
41324       [
41325         "Libya (‫ليبيا‬‎)",
41326         "ly",
41327         "218"
41328       ],
41329       [
41330         "Liechtenstein",
41331         "li",
41332         "423"
41333       ],
41334       [
41335         "Lithuania (Lietuva)",
41336         "lt",
41337         "370"
41338       ],
41339       [
41340         "Luxembourg",
41341         "lu",
41342         "352"
41343       ],
41344       [
41345         "Macau (澳門)",
41346         "mo",
41347         "853"
41348       ],
41349       [
41350         "Macedonia (FYROM) (Македонија)",
41351         "mk",
41352         "389"
41353       ],
41354       [
41355         "Madagascar (Madagasikara)",
41356         "mg",
41357         "261"
41358       ],
41359       [
41360         "Malawi",
41361         "mw",
41362         "265"
41363       ],
41364       [
41365         "Malaysia",
41366         "my",
41367         "60"
41368       ],
41369       [
41370         "Maldives",
41371         "mv",
41372         "960"
41373       ],
41374       [
41375         "Mali",
41376         "ml",
41377         "223"
41378       ],
41379       [
41380         "Malta",
41381         "mt",
41382         "356"
41383       ],
41384       [
41385         "Marshall Islands",
41386         "mh",
41387         "692"
41388       ],
41389       [
41390         "Martinique",
41391         "mq",
41392         "596"
41393       ],
41394       [
41395         "Mauritania (‫موريتانيا‬‎)",
41396         "mr",
41397         "222"
41398       ],
41399       [
41400         "Mauritius (Moris)",
41401         "mu",
41402         "230"
41403       ],
41404       [
41405         "Mayotte",
41406         "yt",
41407         "262",
41408         1
41409       ],
41410       [
41411         "Mexico (México)",
41412         "mx",
41413         "52"
41414       ],
41415       [
41416         "Micronesia",
41417         "fm",
41418         "691"
41419       ],
41420       [
41421         "Moldova (Republica Moldova)",
41422         "md",
41423         "373"
41424       ],
41425       [
41426         "Monaco",
41427         "mc",
41428         "377"
41429       ],
41430       [
41431         "Mongolia (Монгол)",
41432         "mn",
41433         "976"
41434       ],
41435       [
41436         "Montenegro (Crna Gora)",
41437         "me",
41438         "382"
41439       ],
41440       [
41441         "Montserrat",
41442         "ms",
41443         "1664"
41444       ],
41445       [
41446         "Morocco (‫المغرب‬‎)",
41447         "ma",
41448         "212",
41449         0
41450       ],
41451       [
41452         "Mozambique (Moçambique)",
41453         "mz",
41454         "258"
41455       ],
41456       [
41457         "Myanmar (Burma) (မြန်မာ)",
41458         "mm",
41459         "95"
41460       ],
41461       [
41462         "Namibia (Namibië)",
41463         "na",
41464         "264"
41465       ],
41466       [
41467         "Nauru",
41468         "nr",
41469         "674"
41470       ],
41471       [
41472         "Nepal (नेपाल)",
41473         "np",
41474         "977"
41475       ],
41476       [
41477         "Netherlands (Nederland)",
41478         "nl",
41479         "31"
41480       ],
41481       [
41482         "New Caledonia (Nouvelle-Calédonie)",
41483         "nc",
41484         "687"
41485       ],
41486       [
41487         "New Zealand",
41488         "nz",
41489         "64"
41490       ],
41491       [
41492         "Nicaragua",
41493         "ni",
41494         "505"
41495       ],
41496       [
41497         "Niger (Nijar)",
41498         "ne",
41499         "227"
41500       ],
41501       [
41502         "Nigeria",
41503         "ng",
41504         "234"
41505       ],
41506       [
41507         "Niue",
41508         "nu",
41509         "683"
41510       ],
41511       [
41512         "Norfolk Island",
41513         "nf",
41514         "672"
41515       ],
41516       [
41517         "North Korea (조선 민주주의 인민 공화국)",
41518         "kp",
41519         "850"
41520       ],
41521       [
41522         "Northern Mariana Islands",
41523         "mp",
41524         "1670"
41525       ],
41526       [
41527         "Norway (Norge)",
41528         "no",
41529         "47",
41530         0
41531       ],
41532       [
41533         "Oman (‫عُمان‬‎)",
41534         "om",
41535         "968"
41536       ],
41537       [
41538         "Pakistan (‫پاکستان‬‎)",
41539         "pk",
41540         "92"
41541       ],
41542       [
41543         "Palau",
41544         "pw",
41545         "680"
41546       ],
41547       [
41548         "Palestine (‫فلسطين‬‎)",
41549         "ps",
41550         "970"
41551       ],
41552       [
41553         "Panama (Panamá)",
41554         "pa",
41555         "507"
41556       ],
41557       [
41558         "Papua New Guinea",
41559         "pg",
41560         "675"
41561       ],
41562       [
41563         "Paraguay",
41564         "py",
41565         "595"
41566       ],
41567       [
41568         "Peru (Perú)",
41569         "pe",
41570         "51"
41571       ],
41572       [
41573         "Philippines",
41574         "ph",
41575         "63"
41576       ],
41577       [
41578         "Poland (Polska)",
41579         "pl",
41580         "48"
41581       ],
41582       [
41583         "Portugal",
41584         "pt",
41585         "351"
41586       ],
41587       [
41588         "Puerto Rico",
41589         "pr",
41590         "1",
41591         3,
41592         ["787", "939"]
41593       ],
41594       [
41595         "Qatar (‫قطر‬‎)",
41596         "qa",
41597         "974"
41598       ],
41599       [
41600         "Réunion (La Réunion)",
41601         "re",
41602         "262",
41603         0
41604       ],
41605       [
41606         "Romania (România)",
41607         "ro",
41608         "40"
41609       ],
41610       [
41611         "Russia (Россия)",
41612         "ru",
41613         "7",
41614         0
41615       ],
41616       [
41617         "Rwanda",
41618         "rw",
41619         "250"
41620       ],
41621       [
41622         "Saint Barthélemy",
41623         "bl",
41624         "590",
41625         1
41626       ],
41627       [
41628         "Saint Helena",
41629         "sh",
41630         "290"
41631       ],
41632       [
41633         "Saint Kitts and Nevis",
41634         "kn",
41635         "1869"
41636       ],
41637       [
41638         "Saint Lucia",
41639         "lc",
41640         "1758"
41641       ],
41642       [
41643         "Saint Martin (Saint-Martin (partie française))",
41644         "mf",
41645         "590",
41646         2
41647       ],
41648       [
41649         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41650         "pm",
41651         "508"
41652       ],
41653       [
41654         "Saint Vincent and the Grenadines",
41655         "vc",
41656         "1784"
41657       ],
41658       [
41659         "Samoa",
41660         "ws",
41661         "685"
41662       ],
41663       [
41664         "San Marino",
41665         "sm",
41666         "378"
41667       ],
41668       [
41669         "São Tomé and Príncipe (São Tomé e Príncipe)",
41670         "st",
41671         "239"
41672       ],
41673       [
41674         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41675         "sa",
41676         "966"
41677       ],
41678       [
41679         "Senegal (Sénégal)",
41680         "sn",
41681         "221"
41682       ],
41683       [
41684         "Serbia (Србија)",
41685         "rs",
41686         "381"
41687       ],
41688       [
41689         "Seychelles",
41690         "sc",
41691         "248"
41692       ],
41693       [
41694         "Sierra Leone",
41695         "sl",
41696         "232"
41697       ],
41698       [
41699         "Singapore",
41700         "sg",
41701         "65"
41702       ],
41703       [
41704         "Sint Maarten",
41705         "sx",
41706         "1721"
41707       ],
41708       [
41709         "Slovakia (Slovensko)",
41710         "sk",
41711         "421"
41712       ],
41713       [
41714         "Slovenia (Slovenija)",
41715         "si",
41716         "386"
41717       ],
41718       [
41719         "Solomon Islands",
41720         "sb",
41721         "677"
41722       ],
41723       [
41724         "Somalia (Soomaaliya)",
41725         "so",
41726         "252"
41727       ],
41728       [
41729         "South Africa",
41730         "za",
41731         "27"
41732       ],
41733       [
41734         "South Korea (대한민국)",
41735         "kr",
41736         "82"
41737       ],
41738       [
41739         "South Sudan (‫جنوب السودان‬‎)",
41740         "ss",
41741         "211"
41742       ],
41743       [
41744         "Spain (España)",
41745         "es",
41746         "34"
41747       ],
41748       [
41749         "Sri Lanka (ශ්‍රී ලංකාව)",
41750         "lk",
41751         "94"
41752       ],
41753       [
41754         "Sudan (‫السودان‬‎)",
41755         "sd",
41756         "249"
41757       ],
41758       [
41759         "Suriname",
41760         "sr",
41761         "597"
41762       ],
41763       [
41764         "Svalbard and Jan Mayen",
41765         "sj",
41766         "47",
41767         1
41768       ],
41769       [
41770         "Swaziland",
41771         "sz",
41772         "268"
41773       ],
41774       [
41775         "Sweden (Sverige)",
41776         "se",
41777         "46"
41778       ],
41779       [
41780         "Switzerland (Schweiz)",
41781         "ch",
41782         "41"
41783       ],
41784       [
41785         "Syria (‫سوريا‬‎)",
41786         "sy",
41787         "963"
41788       ],
41789       [
41790         "Taiwan (台灣)",
41791         "tw",
41792         "886"
41793       ],
41794       [
41795         "Tajikistan",
41796         "tj",
41797         "992"
41798       ],
41799       [
41800         "Tanzania",
41801         "tz",
41802         "255"
41803       ],
41804       [
41805         "Thailand (ไทย)",
41806         "th",
41807         "66"
41808       ],
41809       [
41810         "Timor-Leste",
41811         "tl",
41812         "670"
41813       ],
41814       [
41815         "Togo",
41816         "tg",
41817         "228"
41818       ],
41819       [
41820         "Tokelau",
41821         "tk",
41822         "690"
41823       ],
41824       [
41825         "Tonga",
41826         "to",
41827         "676"
41828       ],
41829       [
41830         "Trinidad and Tobago",
41831         "tt",
41832         "1868"
41833       ],
41834       [
41835         "Tunisia (‫تونس‬‎)",
41836         "tn",
41837         "216"
41838       ],
41839       [
41840         "Turkey (Türkiye)",
41841         "tr",
41842         "90"
41843       ],
41844       [
41845         "Turkmenistan",
41846         "tm",
41847         "993"
41848       ],
41849       [
41850         "Turks and Caicos Islands",
41851         "tc",
41852         "1649"
41853       ],
41854       [
41855         "Tuvalu",
41856         "tv",
41857         "688"
41858       ],
41859       [
41860         "U.S. Virgin Islands",
41861         "vi",
41862         "1340"
41863       ],
41864       [
41865         "Uganda",
41866         "ug",
41867         "256"
41868       ],
41869       [
41870         "Ukraine (Україна)",
41871         "ua",
41872         "380"
41873       ],
41874       [
41875         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41876         "ae",
41877         "971"
41878       ],
41879       [
41880         "United Kingdom",
41881         "gb",
41882         "44",
41883         0
41884       ],
41885       [
41886         "United States",
41887         "us",
41888         "1",
41889         0
41890       ],
41891       [
41892         "Uruguay",
41893         "uy",
41894         "598"
41895       ],
41896       [
41897         "Uzbekistan (Oʻzbekiston)",
41898         "uz",
41899         "998"
41900       ],
41901       [
41902         "Vanuatu",
41903         "vu",
41904         "678"
41905       ],
41906       [
41907         "Vatican City (Città del Vaticano)",
41908         "va",
41909         "39",
41910         1
41911       ],
41912       [
41913         "Venezuela",
41914         "ve",
41915         "58"
41916       ],
41917       [
41918         "Vietnam (Việt Nam)",
41919         "vn",
41920         "84"
41921       ],
41922       [
41923         "Wallis and Futuna (Wallis-et-Futuna)",
41924         "wf",
41925         "681"
41926       ],
41927       [
41928         "Western Sahara (‫الصحراء الغربية‬‎)",
41929         "eh",
41930         "212",
41931         1
41932       ],
41933       [
41934         "Yemen (‫اليمن‬‎)",
41935         "ye",
41936         "967"
41937       ],
41938       [
41939         "Zambia",
41940         "zm",
41941         "260"
41942       ],
41943       [
41944         "Zimbabwe",
41945         "zw",
41946         "263"
41947       ],
41948       [
41949         "Åland Islands",
41950         "ax",
41951         "358",
41952         1
41953       ]
41954   ];
41955   
41956   return d;
41957 }/**
41958 *    This script refer to:
41959 *    Title: International Telephone Input
41960 *    Author: Jack O'Connor
41961 *    Code version:  v12.1.12
41962 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41963 **/
41964
41965 /**
41966  * @class Roo.bootstrap.PhoneInput
41967  * @extends Roo.bootstrap.TriggerField
41968  * An input with International dial-code selection
41969  
41970  * @cfg {String} defaultDialCode default '+852'
41971  * @cfg {Array} preferedCountries default []
41972   
41973  * @constructor
41974  * Create a new PhoneInput.
41975  * @param {Object} config Configuration options
41976  */
41977
41978 Roo.bootstrap.PhoneInput = function(config) {
41979     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
41980 };
41981
41982 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
41983         
41984         listWidth: undefined,
41985         
41986         selectedClass: 'active',
41987         
41988         invalidClass : "has-warning",
41989         
41990         validClass: 'has-success',
41991         
41992         allowed: '0123456789',
41993         
41994         max_length: 15,
41995         
41996         /**
41997          * @cfg {String} defaultDialCode The default dial code when initializing the input
41998          */
41999         defaultDialCode: '+852',
42000         
42001         /**
42002          * @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
42003          */
42004         preferedCountries: false,
42005         
42006         getAutoCreate : function()
42007         {
42008             var data = Roo.bootstrap.PhoneInputData();
42009             var align = this.labelAlign || this.parentLabelAlign();
42010             var id = Roo.id();
42011             
42012             this.allCountries = [];
42013             this.dialCodeMapping = [];
42014             
42015             for (var i = 0; i < data.length; i++) {
42016               var c = data[i];
42017               this.allCountries[i] = {
42018                 name: c[0],
42019                 iso2: c[1],
42020                 dialCode: c[2],
42021                 priority: c[3] || 0,
42022                 areaCodes: c[4] || null
42023               };
42024               this.dialCodeMapping[c[2]] = {
42025                   name: c[0],
42026                   iso2: c[1],
42027                   priority: c[3] || 0,
42028                   areaCodes: c[4] || null
42029               };
42030             }
42031             
42032             var cfg = {
42033                 cls: 'form-group',
42034                 cn: []
42035             };
42036             
42037             var input =  {
42038                 tag: 'input',
42039                 id : id,
42040                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42041                 maxlength: this.max_length,
42042                 cls : 'form-control tel-input',
42043                 autocomplete: 'new-password'
42044             };
42045             
42046             var hiddenInput = {
42047                 tag: 'input',
42048                 type: 'hidden',
42049                 cls: 'hidden-tel-input'
42050             };
42051             
42052             if (this.name) {
42053                 hiddenInput.name = this.name;
42054             }
42055             
42056             if (this.disabled) {
42057                 input.disabled = true;
42058             }
42059             
42060             var flag_container = {
42061                 tag: 'div',
42062                 cls: 'flag-box',
42063                 cn: [
42064                     {
42065                         tag: 'div',
42066                         cls: 'flag'
42067                     },
42068                     {
42069                         tag: 'div',
42070                         cls: 'caret'
42071                     }
42072                 ]
42073             };
42074             
42075             var box = {
42076                 tag: 'div',
42077                 cls: this.hasFeedback ? 'has-feedback' : '',
42078                 cn: [
42079                     hiddenInput,
42080                     input,
42081                     {
42082                         tag: 'input',
42083                         cls: 'dial-code-holder',
42084                         disabled: true
42085                     }
42086                 ]
42087             };
42088             
42089             var container = {
42090                 cls: 'roo-select2-container input-group',
42091                 cn: [
42092                     flag_container,
42093                     box
42094                 ]
42095             };
42096             
42097             if (this.fieldLabel.length) {
42098                 var indicator = {
42099                     tag: 'i',
42100                     tooltip: 'This field is required'
42101                 };
42102                 
42103                 var label = {
42104                     tag: 'label',
42105                     'for':  id,
42106                     cls: 'control-label',
42107                     cn: []
42108                 };
42109                 
42110                 var label_text = {
42111                     tag: 'span',
42112                     html: this.fieldLabel
42113                 };
42114                 
42115                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42116                 label.cn = [
42117                     indicator,
42118                     label_text
42119                 ];
42120                 
42121                 if(this.indicatorpos == 'right') {
42122                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42123                     label.cn = [
42124                         label_text,
42125                         indicator
42126                     ];
42127                 }
42128                 
42129                 if(align == 'left') {
42130                     container = {
42131                         tag: 'div',
42132                         cn: [
42133                             container
42134                         ]
42135                     };
42136                     
42137                     if(this.labelWidth > 12){
42138                         label.style = "width: " + this.labelWidth + 'px';
42139                     }
42140                     if(this.labelWidth < 13 && this.labelmd == 0){
42141                         this.labelmd = this.labelWidth;
42142                     }
42143                     if(this.labellg > 0){
42144                         label.cls += ' col-lg-' + this.labellg;
42145                         input.cls += ' col-lg-' + (12 - this.labellg);
42146                     }
42147                     if(this.labelmd > 0){
42148                         label.cls += ' col-md-' + this.labelmd;
42149                         container.cls += ' col-md-' + (12 - this.labelmd);
42150                     }
42151                     if(this.labelsm > 0){
42152                         label.cls += ' col-sm-' + this.labelsm;
42153                         container.cls += ' col-sm-' + (12 - this.labelsm);
42154                     }
42155                     if(this.labelxs > 0){
42156                         label.cls += ' col-xs-' + this.labelxs;
42157                         container.cls += ' col-xs-' + (12 - this.labelxs);
42158                     }
42159                 }
42160             }
42161             
42162             cfg.cn = [
42163                 label,
42164                 container
42165             ];
42166             
42167             var settings = this;
42168             
42169             ['xs','sm','md','lg'].map(function(size){
42170                 if (settings[size]) {
42171                     cfg.cls += ' col-' + size + '-' + settings[size];
42172                 }
42173             });
42174             
42175             this.store = new Roo.data.Store({
42176                 proxy : new Roo.data.MemoryProxy({}),
42177                 reader : new Roo.data.JsonReader({
42178                     fields : [
42179                         {
42180                             'name' : 'name',
42181                             'type' : 'string'
42182                         },
42183                         {
42184                             'name' : 'iso2',
42185                             'type' : 'string'
42186                         },
42187                         {
42188                             'name' : 'dialCode',
42189                             'type' : 'string'
42190                         },
42191                         {
42192                             'name' : 'priority',
42193                             'type' : 'string'
42194                         },
42195                         {
42196                             'name' : 'areaCodes',
42197                             'type' : 'string'
42198                         }
42199                     ]
42200                 })
42201             });
42202             
42203             if(!this.preferedCountries) {
42204                 this.preferedCountries = [
42205                     'hk',
42206                     'gb',
42207                     'us'
42208                 ];
42209             }
42210             
42211             var p = this.preferedCountries.reverse();
42212             
42213             if(p) {
42214                 for (var i = 0; i < p.length; i++) {
42215                     for (var j = 0; j < this.allCountries.length; j++) {
42216                         if(this.allCountries[j].iso2 == p[i]) {
42217                             var t = this.allCountries[j];
42218                             this.allCountries.splice(j,1);
42219                             this.allCountries.unshift(t);
42220                         }
42221                     } 
42222                 }
42223             }
42224             
42225             this.store.proxy.data = {
42226                 success: true,
42227                 data: this.allCountries
42228             };
42229             
42230             return cfg;
42231         },
42232         
42233         initEvents : function()
42234         {
42235             this.createList();
42236             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42237             
42238             this.indicator = this.indicatorEl();
42239             this.flag = this.flagEl();
42240             this.dialCodeHolder = this.dialCodeHolderEl();
42241             
42242             this.trigger = this.el.select('div.flag-box',true).first();
42243             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42244             
42245             var _this = this;
42246             
42247             (function(){
42248                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42249                 _this.list.setWidth(lw);
42250             }).defer(100);
42251             
42252             this.list.on('mouseover', this.onViewOver, this);
42253             this.list.on('mousemove', this.onViewMove, this);
42254             this.inputEl().on("keyup", this.onKeyUp, this);
42255             this.inputEl().on("keypress", this.onKeyPress, this);
42256             
42257             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42258
42259             this.view = new Roo.View(this.list, this.tpl, {
42260                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42261             });
42262             
42263             this.view.on('click', this.onViewClick, this);
42264             this.setValue(this.defaultDialCode);
42265         },
42266         
42267         onTriggerClick : function(e)
42268         {
42269             Roo.log('trigger click');
42270             if(this.disabled){
42271                 return;
42272             }
42273             
42274             if(this.isExpanded()){
42275                 this.collapse();
42276                 this.hasFocus = false;
42277             }else {
42278                 this.store.load({});
42279                 this.hasFocus = true;
42280                 this.expand();
42281             }
42282         },
42283         
42284         isExpanded : function()
42285         {
42286             return this.list.isVisible();
42287         },
42288         
42289         collapse : function()
42290         {
42291             if(!this.isExpanded()){
42292                 return;
42293             }
42294             this.list.hide();
42295             Roo.get(document).un('mousedown', this.collapseIf, this);
42296             Roo.get(document).un('mousewheel', this.collapseIf, this);
42297             this.fireEvent('collapse', this);
42298             this.validate();
42299         },
42300         
42301         expand : function()
42302         {
42303             Roo.log('expand');
42304
42305             if(this.isExpanded() || !this.hasFocus){
42306                 return;
42307             }
42308             
42309             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42310             this.list.setWidth(lw);
42311             
42312             this.list.show();
42313             this.restrictHeight();
42314             
42315             Roo.get(document).on('mousedown', this.collapseIf, this);
42316             Roo.get(document).on('mousewheel', this.collapseIf, this);
42317             
42318             this.fireEvent('expand', this);
42319         },
42320         
42321         restrictHeight : function()
42322         {
42323             this.list.alignTo(this.inputEl(), this.listAlign);
42324             this.list.alignTo(this.inputEl(), this.listAlign);
42325         },
42326         
42327         onViewOver : function(e, t)
42328         {
42329             if(this.inKeyMode){
42330                 return;
42331             }
42332             var item = this.view.findItemFromChild(t);
42333             
42334             if(item){
42335                 var index = this.view.indexOf(item);
42336                 this.select(index, false);
42337             }
42338         },
42339
42340         // private
42341         onViewClick : function(view, doFocus, el, e)
42342         {
42343             var index = this.view.getSelectedIndexes()[0];
42344             
42345             var r = this.store.getAt(index);
42346             
42347             if(r){
42348                 this.onSelect(r, index);
42349             }
42350             if(doFocus !== false && !this.blockFocus){
42351                 this.inputEl().focus();
42352             }
42353         },
42354         
42355         onViewMove : function(e, t)
42356         {
42357             this.inKeyMode = false;
42358         },
42359         
42360         select : function(index, scrollIntoView)
42361         {
42362             this.selectedIndex = index;
42363             this.view.select(index);
42364             if(scrollIntoView !== false){
42365                 var el = this.view.getNode(index);
42366                 if(el){
42367                     this.list.scrollChildIntoView(el, false);
42368                 }
42369             }
42370         },
42371         
42372         createList : function()
42373         {
42374             this.list = Roo.get(document.body).createChild({
42375                 tag: 'ul',
42376                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42377                 style: 'display:none'
42378             });
42379             
42380             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42381         },
42382         
42383         collapseIf : function(e)
42384         {
42385             var in_combo  = e.within(this.el);
42386             var in_list =  e.within(this.list);
42387             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42388             
42389             if (in_combo || in_list || is_list) {
42390                 return;
42391             }
42392             this.collapse();
42393         },
42394         
42395         onSelect : function(record, index)
42396         {
42397             if(this.fireEvent('beforeselect', this, record, index) !== false){
42398                 
42399                 this.setFlagClass(record.data.iso2);
42400                 this.setDialCode(record.data.dialCode);
42401                 this.hasFocus = false;
42402                 this.collapse();
42403                 this.fireEvent('select', this, record, index);
42404             }
42405         },
42406         
42407         flagEl : function()
42408         {
42409             var flag = this.el.select('div.flag',true).first();
42410             if(!flag){
42411                 return false;
42412             }
42413             return flag;
42414         },
42415         
42416         dialCodeHolderEl : function()
42417         {
42418             var d = this.el.select('input.dial-code-holder',true).first();
42419             if(!d){
42420                 return false;
42421             }
42422             return d;
42423         },
42424         
42425         setDialCode : function(v)
42426         {
42427             this.dialCodeHolder.dom.value = '+'+v;
42428         },
42429         
42430         setFlagClass : function(n)
42431         {
42432             this.flag.dom.className = 'flag '+n;
42433         },
42434         
42435         getValue : function()
42436         {
42437             var v = this.inputEl().getValue();
42438             if(this.dialCodeHolder) {
42439                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42440             }
42441             return v;
42442         },
42443         
42444         setValue : function(v)
42445         {
42446             var d = this.getDialCode(v);
42447             
42448             //invalid dial code
42449             if(v.length == 0 || !d || d.length == 0) {
42450                 if(this.rendered){
42451                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42452                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42453                 }
42454                 return;
42455             }
42456             
42457             //valid dial code
42458             this.setFlagClass(this.dialCodeMapping[d].iso2);
42459             this.setDialCode(d);
42460             this.inputEl().dom.value = v.replace('+'+d,'');
42461             this.hiddenEl().dom.value = this.getValue();
42462             
42463             this.validate();
42464         },
42465         
42466         getDialCode : function(v)
42467         {
42468             v = v ||  '';
42469             
42470             if (v.length == 0) {
42471                 return this.dialCodeHolder.dom.value;
42472             }
42473             
42474             var dialCode = "";
42475             if (v.charAt(0) != "+") {
42476                 return false;
42477             }
42478             var numericChars = "";
42479             for (var i = 1; i < v.length; i++) {
42480               var c = v.charAt(i);
42481               if (!isNaN(c)) {
42482                 numericChars += c;
42483                 if (this.dialCodeMapping[numericChars]) {
42484                   dialCode = v.substr(1, i);
42485                 }
42486                 if (numericChars.length == 4) {
42487                   break;
42488                 }
42489               }
42490             }
42491             return dialCode;
42492         },
42493         
42494         reset : function()
42495         {
42496             this.setValue(this.defaultDialCode);
42497             this.validate();
42498         },
42499         
42500         hiddenEl : function()
42501         {
42502             return this.el.select('input.hidden-tel-input',true).first();
42503         },
42504         
42505         // after setting val
42506         onKeyUp : function(e){
42507             this.setValue(this.getValue());
42508         },
42509         
42510         onKeyPress : function(e){
42511             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42512                 e.stopEvent();
42513             }
42514         }
42515         
42516 });
42517 /**
42518  * @class Roo.bootstrap.MoneyField
42519  * @extends Roo.bootstrap.ComboBox
42520  * Bootstrap MoneyField class
42521  * 
42522  * @constructor
42523  * Create a new MoneyField.
42524  * @param {Object} config Configuration options
42525  */
42526
42527 Roo.bootstrap.MoneyField = function(config) {
42528     
42529     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42530     
42531 };
42532
42533 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42534     
42535     /**
42536      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42537      */
42538     allowDecimals : true,
42539     /**
42540      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42541      */
42542     decimalSeparator : ".",
42543     /**
42544      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42545      */
42546     decimalPrecision : 0,
42547     /**
42548      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42549      */
42550     allowNegative : true,
42551     /**
42552      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42553      */
42554     allowZero: true,
42555     /**
42556      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42557      */
42558     minValue : Number.NEGATIVE_INFINITY,
42559     /**
42560      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42561      */
42562     maxValue : Number.MAX_VALUE,
42563     /**
42564      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42565      */
42566     minText : "The minimum value for this field is {0}",
42567     /**
42568      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42569      */
42570     maxText : "The maximum value for this field is {0}",
42571     /**
42572      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42573      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42574      */
42575     nanText : "{0} is not a valid number",
42576     /**
42577      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42578      */
42579     castInt : true,
42580     /**
42581      * @cfg {String} defaults currency of the MoneyField
42582      * value should be in lkey
42583      */
42584     defaultCurrency : false,
42585     /**
42586      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42587      */
42588     thousandsDelimiter : false,
42589     /**
42590      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42591      */
42592     max_length: false,
42593     
42594     inputlg : 9,
42595     inputmd : 9,
42596     inputsm : 9,
42597     inputxs : 6,
42598     
42599     store : false,
42600     
42601     getAutoCreate : function()
42602     {
42603         var align = this.labelAlign || this.parentLabelAlign();
42604         
42605         var id = Roo.id();
42606
42607         var cfg = {
42608             cls: 'form-group',
42609             cn: []
42610         };
42611
42612         var input =  {
42613             tag: 'input',
42614             id : id,
42615             cls : 'form-control roo-money-amount-input',
42616             autocomplete: 'new-password'
42617         };
42618         
42619         var hiddenInput = {
42620             tag: 'input',
42621             type: 'hidden',
42622             id: Roo.id(),
42623             cls: 'hidden-number-input'
42624         };
42625         
42626         if(this.max_length) {
42627             input.maxlength = this.max_length; 
42628         }
42629         
42630         if (this.name) {
42631             hiddenInput.name = this.name;
42632         }
42633
42634         if (this.disabled) {
42635             input.disabled = true;
42636         }
42637
42638         var clg = 12 - this.inputlg;
42639         var cmd = 12 - this.inputmd;
42640         var csm = 12 - this.inputsm;
42641         var cxs = 12 - this.inputxs;
42642         
42643         var container = {
42644             tag : 'div',
42645             cls : 'row roo-money-field',
42646             cn : [
42647                 {
42648                     tag : 'div',
42649                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42650                     cn : [
42651                         {
42652                             tag : 'div',
42653                             cls: 'roo-select2-container input-group',
42654                             cn: [
42655                                 {
42656                                     tag : 'input',
42657                                     cls : 'form-control roo-money-currency-input',
42658                                     autocomplete: 'new-password',
42659                                     readOnly : 1,
42660                                     name : this.currencyName
42661                                 },
42662                                 {
42663                                     tag :'span',
42664                                     cls : 'input-group-addon',
42665                                     cn : [
42666                                         {
42667                                             tag: 'span',
42668                                             cls: 'caret'
42669                                         }
42670                                     ]
42671                                 }
42672                             ]
42673                         }
42674                     ]
42675                 },
42676                 {
42677                     tag : 'div',
42678                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42679                     cn : [
42680                         {
42681                             tag: 'div',
42682                             cls: this.hasFeedback ? 'has-feedback' : '',
42683                             cn: [
42684                                 input
42685                             ]
42686                         }
42687                     ]
42688                 }
42689             ]
42690             
42691         };
42692         
42693         if (this.fieldLabel.length) {
42694             var indicator = {
42695                 tag: 'i',
42696                 tooltip: 'This field is required'
42697             };
42698
42699             var label = {
42700                 tag: 'label',
42701                 'for':  id,
42702                 cls: 'control-label',
42703                 cn: []
42704             };
42705
42706             var label_text = {
42707                 tag: 'span',
42708                 html: this.fieldLabel
42709             };
42710
42711             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42712             label.cn = [
42713                 indicator,
42714                 label_text
42715             ];
42716
42717             if(this.indicatorpos == 'right') {
42718                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42719                 label.cn = [
42720                     label_text,
42721                     indicator
42722                 ];
42723             }
42724
42725             if(align == 'left') {
42726                 container = {
42727                     tag: 'div',
42728                     cn: [
42729                         container
42730                     ]
42731                 };
42732
42733                 if(this.labelWidth > 12){
42734                     label.style = "width: " + this.labelWidth + 'px';
42735                 }
42736                 if(this.labelWidth < 13 && this.labelmd == 0){
42737                     this.labelmd = this.labelWidth;
42738                 }
42739                 if(this.labellg > 0){
42740                     label.cls += ' col-lg-' + this.labellg;
42741                     input.cls += ' col-lg-' + (12 - this.labellg);
42742                 }
42743                 if(this.labelmd > 0){
42744                     label.cls += ' col-md-' + this.labelmd;
42745                     container.cls += ' col-md-' + (12 - this.labelmd);
42746                 }
42747                 if(this.labelsm > 0){
42748                     label.cls += ' col-sm-' + this.labelsm;
42749                     container.cls += ' col-sm-' + (12 - this.labelsm);
42750                 }
42751                 if(this.labelxs > 0){
42752                     label.cls += ' col-xs-' + this.labelxs;
42753                     container.cls += ' col-xs-' + (12 - this.labelxs);
42754                 }
42755             }
42756         }
42757
42758         cfg.cn = [
42759             label,
42760             container,
42761             hiddenInput
42762         ];
42763         
42764         var settings = this;
42765
42766         ['xs','sm','md','lg'].map(function(size){
42767             if (settings[size]) {
42768                 cfg.cls += ' col-' + size + '-' + settings[size];
42769             }
42770         });
42771         
42772         return cfg;
42773     },
42774     
42775     initEvents : function()
42776     {
42777         this.indicator = this.indicatorEl();
42778         
42779         this.initCurrencyEvent();
42780         
42781         this.initNumberEvent();
42782     },
42783     
42784     initCurrencyEvent : function()
42785     {
42786         if (!this.store) {
42787             throw "can not find store for combo";
42788         }
42789         
42790         this.store = Roo.factory(this.store, Roo.data);
42791         this.store.parent = this;
42792         
42793         this.createList();
42794         
42795         this.triggerEl = this.el.select('.input-group-addon', true).first();
42796         
42797         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42798         
42799         var _this = this;
42800         
42801         (function(){
42802             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42803             _this.list.setWidth(lw);
42804         }).defer(100);
42805         
42806         this.list.on('mouseover', this.onViewOver, this);
42807         this.list.on('mousemove', this.onViewMove, this);
42808         this.list.on('scroll', this.onViewScroll, this);
42809         
42810         if(!this.tpl){
42811             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42812         }
42813         
42814         this.view = new Roo.View(this.list, this.tpl, {
42815             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42816         });
42817         
42818         this.view.on('click', this.onViewClick, this);
42819         
42820         this.store.on('beforeload', this.onBeforeLoad, this);
42821         this.store.on('load', this.onLoad, this);
42822         this.store.on('loadexception', this.onLoadException, this);
42823         
42824         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42825             "up" : function(e){
42826                 this.inKeyMode = true;
42827                 this.selectPrev();
42828             },
42829
42830             "down" : function(e){
42831                 if(!this.isExpanded()){
42832                     this.onTriggerClick();
42833                 }else{
42834                     this.inKeyMode = true;
42835                     this.selectNext();
42836                 }
42837             },
42838
42839             "enter" : function(e){
42840                 this.collapse();
42841                 
42842                 if(this.fireEvent("specialkey", this, e)){
42843                     this.onViewClick(false);
42844                 }
42845                 
42846                 return true;
42847             },
42848
42849             "esc" : function(e){
42850                 this.collapse();
42851             },
42852
42853             "tab" : function(e){
42854                 this.collapse();
42855                 
42856                 if(this.fireEvent("specialkey", this, e)){
42857                     this.onViewClick(false);
42858                 }
42859                 
42860                 return true;
42861             },
42862
42863             scope : this,
42864
42865             doRelay : function(foo, bar, hname){
42866                 if(hname == 'down' || this.scope.isExpanded()){
42867                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42868                 }
42869                 return true;
42870             },
42871
42872             forceKeyDown: true
42873         });
42874         
42875         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42876         
42877     },
42878     
42879     initNumberEvent : function(e)
42880     {
42881         this.inputEl().on("keydown" , this.fireKey,  this);
42882         this.inputEl().on("focus", this.onFocus,  this);
42883         this.inputEl().on("blur", this.onBlur,  this);
42884         
42885         this.inputEl().relayEvent('keyup', this);
42886         
42887         if(this.indicator){
42888             this.indicator.addClass('invisible');
42889         }
42890  
42891         this.originalValue = this.getValue();
42892         
42893         if(this.validationEvent == 'keyup'){
42894             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42895             this.inputEl().on('keyup', this.filterValidation, this);
42896         }
42897         else if(this.validationEvent !== false){
42898             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42899         }
42900         
42901         if(this.selectOnFocus){
42902             this.on("focus", this.preFocus, this);
42903             
42904         }
42905         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42906             this.inputEl().on("keypress", this.filterKeys, this);
42907         } else {
42908             this.inputEl().relayEvent('keypress', this);
42909         }
42910         
42911         var allowed = "0123456789";
42912         
42913         if(this.allowDecimals){
42914             allowed += this.decimalSeparator;
42915         }
42916         
42917         if(this.allowNegative){
42918             allowed += "-";
42919         }
42920         
42921         if(this.thousandsDelimiter) {
42922             allowed += ",";
42923         }
42924         
42925         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42926         
42927         var keyPress = function(e){
42928             
42929             var k = e.getKey();
42930             
42931             var c = e.getCharCode();
42932             
42933             if(
42934                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42935                     allowed.indexOf(String.fromCharCode(c)) === -1
42936             ){
42937                 e.stopEvent();
42938                 return;
42939             }
42940             
42941             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42942                 return;
42943             }
42944             
42945             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42946                 e.stopEvent();
42947             }
42948         };
42949         
42950         this.inputEl().on("keypress", keyPress, this);
42951         
42952     },
42953     
42954     onTriggerClick : function(e)
42955     {   
42956         if(this.disabled){
42957             return;
42958         }
42959         
42960         this.page = 0;
42961         this.loadNext = false;
42962         
42963         if(this.isExpanded()){
42964             this.collapse();
42965             return;
42966         }
42967         
42968         this.hasFocus = true;
42969         
42970         if(this.triggerAction == 'all') {
42971             this.doQuery(this.allQuery, true);
42972             return;
42973         }
42974         
42975         this.doQuery(this.getRawValue());
42976     },
42977     
42978     getCurrency : function()
42979     {   
42980         var v = this.currencyEl().getValue();
42981         
42982         return v;
42983     },
42984     
42985     restrictHeight : function()
42986     {
42987         this.list.alignTo(this.currencyEl(), this.listAlign);
42988         this.list.alignTo(this.currencyEl(), this.listAlign);
42989     },
42990     
42991     onViewClick : function(view, doFocus, el, e)
42992     {
42993         var index = this.view.getSelectedIndexes()[0];
42994         
42995         var r = this.store.getAt(index);
42996         
42997         if(r){
42998             this.onSelect(r, index);
42999         }
43000     },
43001     
43002     onSelect : function(record, index){
43003         
43004         if(this.fireEvent('beforeselect', this, record, index) !== false){
43005         
43006             this.setFromCurrencyData(index > -1 ? record.data : false);
43007             
43008             this.collapse();
43009             
43010             this.fireEvent('select', this, record, index);
43011         }
43012     },
43013     
43014     setFromCurrencyData : function(o)
43015     {
43016         var currency = '';
43017         
43018         this.lastCurrency = o;
43019         
43020         if (this.currencyField) {
43021             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43022         } else {
43023             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43024         }
43025         
43026         this.lastSelectionText = currency;
43027         
43028         //setting default currency
43029         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43030             this.setCurrency(this.defaultCurrency);
43031             return;
43032         }
43033         
43034         this.setCurrency(currency);
43035     },
43036     
43037     setFromData : function(o)
43038     {
43039         var c = {};
43040         
43041         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43042         
43043         this.setFromCurrencyData(c);
43044         
43045         var value = '';
43046         
43047         if (this.name) {
43048             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43049         } else {
43050             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43051         }
43052         
43053         this.setValue(value);
43054         
43055     },
43056     
43057     setCurrency : function(v)
43058     {   
43059         this.currencyValue = v;
43060         
43061         if(this.rendered){
43062             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43063             this.validate();
43064         }
43065     },
43066     
43067     setValue : function(v)
43068     {
43069         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43070         
43071         this.value = v;
43072         
43073         if(this.rendered){
43074             
43075             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43076             
43077             this.inputEl().dom.value = (v == '') ? '' :
43078                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43079             
43080             if(!this.allowZero && v === '0') {
43081                 this.hiddenEl().dom.value = '';
43082                 this.inputEl().dom.value = '';
43083             }
43084             
43085             this.validate();
43086         }
43087     },
43088     
43089     getRawValue : function()
43090     {
43091         var v = this.inputEl().getValue();
43092         
43093         return v;
43094     },
43095     
43096     getValue : function()
43097     {
43098         return this.fixPrecision(this.parseValue(this.getRawValue()));
43099     },
43100     
43101     parseValue : function(value)
43102     {
43103         if(this.thousandsDelimiter) {
43104             value += "";
43105             r = new RegExp(",", "g");
43106             value = value.replace(r, "");
43107         }
43108         
43109         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43110         return isNaN(value) ? '' : value;
43111         
43112     },
43113     
43114     fixPrecision : function(value)
43115     {
43116         if(this.thousandsDelimiter) {
43117             value += "";
43118             r = new RegExp(",", "g");
43119             value = value.replace(r, "");
43120         }
43121         
43122         var nan = isNaN(value);
43123         
43124         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43125             return nan ? '' : value;
43126         }
43127         return parseFloat(value).toFixed(this.decimalPrecision);
43128     },
43129     
43130     decimalPrecisionFcn : function(v)
43131     {
43132         return Math.floor(v);
43133     },
43134     
43135     validateValue : function(value)
43136     {
43137         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43138             return false;
43139         }
43140         
43141         var num = this.parseValue(value);
43142         
43143         if(isNaN(num)){
43144             this.markInvalid(String.format(this.nanText, value));
43145             return false;
43146         }
43147         
43148         if(num < this.minValue){
43149             this.markInvalid(String.format(this.minText, this.minValue));
43150             return false;
43151         }
43152         
43153         if(num > this.maxValue){
43154             this.markInvalid(String.format(this.maxText, this.maxValue));
43155             return false;
43156         }
43157         
43158         return true;
43159     },
43160     
43161     validate : function()
43162     {
43163         if(this.disabled || this.allowBlank){
43164             this.markValid();
43165             return true;
43166         }
43167         
43168         var currency = this.getCurrency();
43169         
43170         if(this.validateValue(this.getRawValue()) && currency.length){
43171             this.markValid();
43172             return true;
43173         }
43174         
43175         this.markInvalid();
43176         return false;
43177     },
43178     
43179     getName: function()
43180     {
43181         return this.name;
43182     },
43183     
43184     beforeBlur : function()
43185     {
43186         if(!this.castInt){
43187             return;
43188         }
43189         
43190         var v = this.parseValue(this.getRawValue());
43191         
43192         if(v || v == 0){
43193             this.setValue(v);
43194         }
43195     },
43196     
43197     onBlur : function()
43198     {
43199         this.beforeBlur();
43200         
43201         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43202             //this.el.removeClass(this.focusClass);
43203         }
43204         
43205         this.hasFocus = false;
43206         
43207         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43208             this.validate();
43209         }
43210         
43211         var v = this.getValue();
43212         
43213         if(String(v) !== String(this.startValue)){
43214             this.fireEvent('change', this, v, this.startValue);
43215         }
43216         
43217         this.fireEvent("blur", this);
43218     },
43219     
43220     inputEl : function()
43221     {
43222         return this.el.select('.roo-money-amount-input', true).first();
43223     },
43224     
43225     currencyEl : function()
43226     {
43227         return this.el.select('.roo-money-currency-input', true).first();
43228     },
43229     
43230     hiddenEl : function()
43231     {
43232         return this.el.select('input.hidden-number-input',true).first();
43233     }
43234     
43235 });/**
43236  * @class Roo.bootstrap.BezierSignature
43237  * @extends Roo.bootstrap.Component
43238  * Bootstrap BezierSignature class
43239  * This script refer to:
43240  *    Title: Signature Pad
43241  *    Author: szimek
43242  *    Availability: https://github.com/szimek/signature_pad
43243  *
43244  * @constructor
43245  * Create a new BezierSignature
43246  * @param {Object} config The config object
43247  */
43248
43249 Roo.bootstrap.BezierSignature = function(config){
43250     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43251     this.addEvents({
43252         "resize" : true
43253     });
43254 };
43255
43256 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43257 {
43258      
43259     curve_data: [],
43260     
43261     is_empty: true,
43262     
43263     mouse_btn_down: true,
43264     
43265     /**
43266      * @cfg {int} canvas height
43267      */
43268     canvas_height: '200px',
43269     
43270     /**
43271      * @cfg {float|function} Radius of a single dot.
43272      */ 
43273     dot_size: false,
43274     
43275     /**
43276      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43277      */
43278     min_width: 0.5,
43279     
43280     /**
43281      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43282      */
43283     max_width: 2.5,
43284     
43285     /**
43286      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43287      */
43288     throttle: 16,
43289     
43290     /**
43291      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43292      */
43293     min_distance: 5,
43294     
43295     /**
43296      * @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.
43297      */
43298     bg_color: 'rgba(0, 0, 0, 0)',
43299     
43300     /**
43301      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43302      */
43303     dot_color: 'black',
43304     
43305     /**
43306      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43307      */ 
43308     velocity_filter_weight: 0.7,
43309     
43310     /**
43311      * @cfg {function} Callback when stroke begin. 
43312      */
43313     onBegin: false,
43314     
43315     /**
43316      * @cfg {function} Callback when stroke end.
43317      */
43318     onEnd: false,
43319     
43320     getAutoCreate : function()
43321     {
43322         var cls = 'roo-signature column';
43323         
43324         if(this.cls){
43325             cls += ' ' + this.cls;
43326         }
43327         
43328         var col_sizes = [
43329             'lg',
43330             'md',
43331             'sm',
43332             'xs'
43333         ];
43334         
43335         for(var i = 0; i < col_sizes.length; i++) {
43336             if(this[col_sizes[i]]) {
43337                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43338             }
43339         }
43340         
43341         var cfg = {
43342             tag: 'div',
43343             cls: cls,
43344             cn: [
43345                 {
43346                     tag: 'div',
43347                     cls: 'roo-signature-body',
43348                     cn: [
43349                         {
43350                             tag: 'canvas',
43351                             cls: 'roo-signature-body-canvas',
43352                             height: this.canvas_height,
43353                             width: this.canvas_width
43354                         }
43355                     ]
43356                 },
43357                 {
43358                     tag: 'input',
43359                     type: 'file',
43360                     style: 'display: none'
43361                 }
43362             ]
43363         };
43364         
43365         return cfg;
43366     },
43367     
43368     initEvents: function() 
43369     {
43370         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43371         
43372         var canvas = this.canvasEl();
43373         
43374         // mouse && touch event swapping...
43375         canvas.dom.style.touchAction = 'none';
43376         canvas.dom.style.msTouchAction = 'none';
43377         
43378         this.mouse_btn_down = false;
43379         canvas.on('mousedown', this._handleMouseDown, this);
43380         canvas.on('mousemove', this._handleMouseMove, this);
43381         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43382         
43383         if (window.PointerEvent) {
43384             canvas.on('pointerdown', this._handleMouseDown, this);
43385             canvas.on('pointermove', this._handleMouseMove, this);
43386             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43387         }
43388         
43389         if ('ontouchstart' in window) {
43390             canvas.on('touchstart', this._handleTouchStart, this);
43391             canvas.on('touchmove', this._handleTouchMove, this);
43392             canvas.on('touchend', this._handleTouchEnd, this);
43393         }
43394         
43395         Roo.EventManager.onWindowResize(this.resize, this, true);
43396         
43397         // file input event
43398         this.fileEl().on('change', this.uploadImage, this);
43399         
43400         this.clear();
43401         
43402         this.resize();
43403     },
43404     
43405     resize: function(){
43406         
43407         var canvas = this.canvasEl().dom;
43408         var ctx = this.canvasElCtx();
43409         var img_data = false;
43410         
43411         if(canvas.width > 0) {
43412             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43413         }
43414         // setting canvas width will clean img data
43415         canvas.width = 0;
43416         
43417         var style = window.getComputedStyle ? 
43418             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43419             
43420         var padding_left = parseInt(style.paddingLeft) || 0;
43421         var padding_right = parseInt(style.paddingRight) || 0;
43422         
43423         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43424         
43425         if(img_data) {
43426             ctx.putImageData(img_data, 0, 0);
43427         }
43428     },
43429     
43430     _handleMouseDown: function(e)
43431     {
43432         if (e.browserEvent.which === 1) {
43433             this.mouse_btn_down = true;
43434             this.strokeBegin(e);
43435         }
43436     },
43437     
43438     _handleMouseMove: function (e)
43439     {
43440         if (this.mouse_btn_down) {
43441             this.strokeMoveUpdate(e);
43442         }
43443     },
43444     
43445     _handleMouseUp: function (e)
43446     {
43447         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43448             this.mouse_btn_down = false;
43449             this.strokeEnd(e);
43450         }
43451     },
43452     
43453     _handleTouchStart: function (e) {
43454         
43455         e.preventDefault();
43456         if (e.browserEvent.targetTouches.length === 1) {
43457             // var touch = e.browserEvent.changedTouches[0];
43458             // this.strokeBegin(touch);
43459             
43460              this.strokeBegin(e); // assume e catching the correct xy...
43461         }
43462     },
43463     
43464     _handleTouchMove: function (e) {
43465         e.preventDefault();
43466         // var touch = event.targetTouches[0];
43467         // _this._strokeMoveUpdate(touch);
43468         this.strokeMoveUpdate(e);
43469     },
43470     
43471     _handleTouchEnd: function (e) {
43472         var wasCanvasTouched = e.target === this.canvasEl().dom;
43473         if (wasCanvasTouched) {
43474             e.preventDefault();
43475             // var touch = event.changedTouches[0];
43476             // _this._strokeEnd(touch);
43477             this.strokeEnd(e);
43478         }
43479     },
43480     
43481     reset: function () {
43482         this._lastPoints = [];
43483         this._lastVelocity = 0;
43484         this._lastWidth = (this.min_width + this.max_width) / 2;
43485         this.canvasElCtx().fillStyle = this.dot_color;
43486     },
43487     
43488     strokeMoveUpdate: function(e)
43489     {
43490         this.strokeUpdate(e);
43491         
43492         if (this.throttle) {
43493             this.throttleStroke(this.strokeUpdate, this.throttle);
43494         }
43495         else {
43496             this.strokeUpdate(e);
43497         }
43498     },
43499     
43500     strokeBegin: function(e)
43501     {
43502         var newPointGroup = {
43503             color: this.dot_color,
43504             points: []
43505         };
43506         
43507         if (typeof this.onBegin === 'function') {
43508             this.onBegin(e);
43509         }
43510         
43511         this.curve_data.push(newPointGroup);
43512         this.reset();
43513         this.strokeUpdate(e);
43514     },
43515     
43516     strokeUpdate: function(e)
43517     {
43518         var rect = this.canvasEl().dom.getBoundingClientRect();
43519         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43520         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43521         var lastPoints = lastPointGroup.points;
43522         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43523         var isLastPointTooClose = lastPoint
43524             ? point.distanceTo(lastPoint) <= this.min_distance
43525             : false;
43526         var color = lastPointGroup.color;
43527         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43528             var curve = this.addPoint(point);
43529             if (!lastPoint) {
43530                 this.drawDot({color: color, point: point});
43531             }
43532             else if (curve) {
43533                 this.drawCurve({color: color, curve: curve});
43534             }
43535             lastPoints.push({
43536                 time: point.time,
43537                 x: point.x,
43538                 y: point.y
43539             });
43540         }
43541     },
43542     
43543     strokeEnd: function(e)
43544     {
43545         this.strokeUpdate(e);
43546         if (typeof this.onEnd === 'function') {
43547             this.onEnd(e);
43548         }
43549     },
43550     
43551     addPoint:  function (point) {
43552         var _lastPoints = this._lastPoints;
43553         _lastPoints.push(point);
43554         if (_lastPoints.length > 2) {
43555             if (_lastPoints.length === 3) {
43556                 _lastPoints.unshift(_lastPoints[0]);
43557             }
43558             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43559             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43560             _lastPoints.shift();
43561             return curve;
43562         }
43563         return null;
43564     },
43565     
43566     calculateCurveWidths: function (startPoint, endPoint) {
43567         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43568             (1 - this.velocity_filter_weight) * this._lastVelocity;
43569
43570         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43571         var widths = {
43572             end: newWidth,
43573             start: this._lastWidth
43574         };
43575         
43576         this._lastVelocity = velocity;
43577         this._lastWidth = newWidth;
43578         return widths;
43579     },
43580     
43581     drawDot: function (_a) {
43582         var color = _a.color, point = _a.point;
43583         var ctx = this.canvasElCtx();
43584         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43585         ctx.beginPath();
43586         this.drawCurveSegment(point.x, point.y, width);
43587         ctx.closePath();
43588         ctx.fillStyle = color;
43589         ctx.fill();
43590     },
43591     
43592     drawCurve: function (_a) {
43593         var color = _a.color, curve = _a.curve;
43594         var ctx = this.canvasElCtx();
43595         var widthDelta = curve.endWidth - curve.startWidth;
43596         var drawSteps = Math.floor(curve.length()) * 2;
43597         ctx.beginPath();
43598         ctx.fillStyle = color;
43599         for (var i = 0; i < drawSteps; i += 1) {
43600         var t = i / drawSteps;
43601         var tt = t * t;
43602         var ttt = tt * t;
43603         var u = 1 - t;
43604         var uu = u * u;
43605         var uuu = uu * u;
43606         var x = uuu * curve.startPoint.x;
43607         x += 3 * uu * t * curve.control1.x;
43608         x += 3 * u * tt * curve.control2.x;
43609         x += ttt * curve.endPoint.x;
43610         var y = uuu * curve.startPoint.y;
43611         y += 3 * uu * t * curve.control1.y;
43612         y += 3 * u * tt * curve.control2.y;
43613         y += ttt * curve.endPoint.y;
43614         var width = curve.startWidth + ttt * widthDelta;
43615         this.drawCurveSegment(x, y, width);
43616         }
43617         ctx.closePath();
43618         ctx.fill();
43619     },
43620     
43621     drawCurveSegment: function (x, y, width) {
43622         var ctx = this.canvasElCtx();
43623         ctx.moveTo(x, y);
43624         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43625         this.is_empty = false;
43626     },
43627     
43628     clear: function()
43629     {
43630         var ctx = this.canvasElCtx();
43631         var canvas = this.canvasEl().dom;
43632         ctx.fillStyle = this.bg_color;
43633         ctx.clearRect(0, 0, canvas.width, canvas.height);
43634         ctx.fillRect(0, 0, canvas.width, canvas.height);
43635         this.curve_data = [];
43636         this.reset();
43637         this.is_empty = true;
43638     },
43639     
43640     fileEl: function()
43641     {
43642         return  this.el.select('input',true).first();
43643     },
43644     
43645     canvasEl: function()
43646     {
43647         return this.el.select('canvas',true).first();
43648     },
43649     
43650     canvasElCtx: function()
43651     {
43652         return this.el.select('canvas',true).first().dom.getContext('2d');
43653     },
43654     
43655     getImage: function(type)
43656     {
43657         if(this.is_empty) {
43658             return false;
43659         }
43660         
43661         // encryption ?
43662         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43663     },
43664     
43665     drawFromImage: function(img_src)
43666     {
43667         var img = new Image();
43668         
43669         img.onload = function(){
43670             this.canvasElCtx().drawImage(img, 0, 0);
43671         }.bind(this);
43672         
43673         img.src = img_src;
43674         
43675         this.is_empty = false;
43676     },
43677     
43678     selectImage: function()
43679     {
43680         this.fileEl().dom.click();
43681     },
43682     
43683     uploadImage: function(e)
43684     {
43685         var reader = new FileReader();
43686         
43687         reader.onload = function(e){
43688             var img = new Image();
43689             img.onload = function(){
43690                 this.reset();
43691                 this.canvasElCtx().drawImage(img, 0, 0);
43692             }.bind(this);
43693             img.src = e.target.result;
43694         }.bind(this);
43695         
43696         reader.readAsDataURL(e.target.files[0]);
43697     },
43698     
43699     // Bezier Point Constructor
43700     Point: (function () {
43701         function Point(x, y, time) {
43702             this.x = x;
43703             this.y = y;
43704             this.time = time || Date.now();
43705         }
43706         Point.prototype.distanceTo = function (start) {
43707             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43708         };
43709         Point.prototype.equals = function (other) {
43710             return this.x === other.x && this.y === other.y && this.time === other.time;
43711         };
43712         Point.prototype.velocityFrom = function (start) {
43713             return this.time !== start.time
43714             ? this.distanceTo(start) / (this.time - start.time)
43715             : 0;
43716         };
43717         return Point;
43718     }()),
43719     
43720     
43721     // Bezier Constructor
43722     Bezier: (function () {
43723         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43724             this.startPoint = startPoint;
43725             this.control2 = control2;
43726             this.control1 = control1;
43727             this.endPoint = endPoint;
43728             this.startWidth = startWidth;
43729             this.endWidth = endWidth;
43730         }
43731         Bezier.fromPoints = function (points, widths, scope) {
43732             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43733             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43734             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43735         };
43736         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43737             var dx1 = s1.x - s2.x;
43738             var dy1 = s1.y - s2.y;
43739             var dx2 = s2.x - s3.x;
43740             var dy2 = s2.y - s3.y;
43741             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43742             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43743             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43744             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43745             var dxm = m1.x - m2.x;
43746             var dym = m1.y - m2.y;
43747             var k = l2 / (l1 + l2);
43748             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43749             var tx = s2.x - cm.x;
43750             var ty = s2.y - cm.y;
43751             return {
43752                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43753                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43754             };
43755         };
43756         Bezier.prototype.length = function () {
43757             var steps = 10;
43758             var length = 0;
43759             var px;
43760             var py;
43761             for (var i = 0; i <= steps; i += 1) {
43762                 var t = i / steps;
43763                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43764                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43765                 if (i > 0) {
43766                     var xdiff = cx - px;
43767                     var ydiff = cy - py;
43768                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43769                 }
43770                 px = cx;
43771                 py = cy;
43772             }
43773             return length;
43774         };
43775         Bezier.prototype.point = function (t, start, c1, c2, end) {
43776             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43777             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43778             + (3.0 * c2 * (1.0 - t) * t * t)
43779             + (end * t * t * t);
43780         };
43781         return Bezier;
43782     }()),
43783     
43784     throttleStroke: function(fn, wait) {
43785       if (wait === void 0) { wait = 250; }
43786       var previous = 0;
43787       var timeout = null;
43788       var result;
43789       var storedContext;
43790       var storedArgs;
43791       var later = function () {
43792           previous = Date.now();
43793           timeout = null;
43794           result = fn.apply(storedContext, storedArgs);
43795           if (!timeout) {
43796               storedContext = null;
43797               storedArgs = [];
43798           }
43799       };
43800       return function wrapper() {
43801           var args = [];
43802           for (var _i = 0; _i < arguments.length; _i++) {
43803               args[_i] = arguments[_i];
43804           }
43805           var now = Date.now();
43806           var remaining = wait - (now - previous);
43807           storedContext = this;
43808           storedArgs = args;
43809           if (remaining <= 0 || remaining > wait) {
43810               if (timeout) {
43811                   clearTimeout(timeout);
43812                   timeout = null;
43813               }
43814               previous = now;
43815               result = fn.apply(storedContext, storedArgs);
43816               if (!timeout) {
43817                   storedContext = null;
43818                   storedArgs = [];
43819               }
43820           }
43821           else if (!timeout) {
43822               timeout = window.setTimeout(later, remaining);
43823           }
43824           return result;
43825       };
43826   }
43827   
43828 });
43829
43830  
43831
43832