buildSDK/dependancy_bootstrap.txt
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<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>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <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>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
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 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9161  * @cfg {Boolean} rowSelection (true|false) default false
9162  * @cfg {Boolean} cellSelection (true|false) default false
9163  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9164  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9165  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9166  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9167  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9168  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9169  *
9170  * 
9171  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9172  * 
9173  * @constructor
9174  * Create a new Table
9175  * @param {Object} config The config object
9176  */
9177
9178 Roo.bootstrap.Table = function(config)
9179 {
9180     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9181      
9182     // BC...
9183     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9187     
9188     this.view = this; // compat with grid.
9189     
9190     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9191     if (this.sm) {
9192         this.sm.grid = this;
9193         this.selModel = Roo.factory(this.sm, Roo.grid);
9194         this.sm = this.selModel;
9195         this.sm.xmodule = this.xmodule || false;
9196     }
9197     
9198     if (this.cm && typeof(this.cm.config) == 'undefined') {
9199         this.colModel = new Roo.grid.ColumnModel(this.cm);
9200         this.cm = this.colModel;
9201         this.cm.xmodule = this.xmodule || false;
9202     }
9203     if (this.store) {
9204         this.store= Roo.factory(this.store, Roo.data);
9205         this.ds = this.store;
9206         this.ds.xmodule = this.xmodule || false;
9207          
9208     }
9209     if (this.footer && this.store) {
9210         this.footer.dataSource = this.ds;
9211         this.footer = Roo.factory(this.footer);
9212     }
9213     
9214     /** @private */
9215     this.addEvents({
9216         /**
9217          * @event cellclick
9218          * Fires when a cell is clicked
9219          * @param {Roo.bootstrap.Table} this
9220          * @param {Roo.Element} el
9221          * @param {Number} rowIndex
9222          * @param {Number} columnIndex
9223          * @param {Roo.EventObject} e
9224          */
9225         "cellclick" : true,
9226         /**
9227          * @event celldblclick
9228          * Fires when a cell is double clicked
9229          * @param {Roo.bootstrap.Table} this
9230          * @param {Roo.Element} el
9231          * @param {Number} rowIndex
9232          * @param {Number} columnIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "celldblclick" : true,
9236         /**
9237          * @event rowclick
9238          * Fires when a row is clicked
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Roo.EventObject} e
9243          */
9244         "rowclick" : true,
9245         /**
9246          * @event rowdblclick
9247          * Fires when a row is double clicked
9248          * @param {Roo.bootstrap.Table} this
9249          * @param {Roo.Element} el
9250          * @param {Number} rowIndex
9251          * @param {Roo.EventObject} e
9252          */
9253         "rowdblclick" : true,
9254         /**
9255          * @event mouseover
9256          * Fires when a mouseover occur
9257          * @param {Roo.bootstrap.Table} this
9258          * @param {Roo.Element} el
9259          * @param {Number} rowIndex
9260          * @param {Number} columnIndex
9261          * @param {Roo.EventObject} e
9262          */
9263         "mouseover" : true,
9264         /**
9265          * @event mouseout
9266          * Fires when a mouseout occur
9267          * @param {Roo.bootstrap.Table} this
9268          * @param {Roo.Element} el
9269          * @param {Number} rowIndex
9270          * @param {Number} columnIndex
9271          * @param {Roo.EventObject} e
9272          */
9273         "mouseout" : true,
9274         /**
9275          * @event rowclass
9276          * Fires when a row is rendered, so you can change add a style to it.
9277          * @param {Roo.bootstrap.Table} this
9278          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9279          */
9280         'rowclass' : true,
9281           /**
9282          * @event rowsrendered
9283          * Fires when all the  rows have been rendered
9284          * @param {Roo.bootstrap.Table} this
9285          */
9286         'rowsrendered' : true,
9287         /**
9288          * @event contextmenu
9289          * The raw contextmenu event for the entire grid.
9290          * @param {Roo.EventObject} e
9291          */
9292         "contextmenu" : true,
9293         /**
9294          * @event rowcontextmenu
9295          * Fires when a row is right clicked
9296          * @param {Roo.bootstrap.Table} this
9297          * @param {Number} rowIndex
9298          * @param {Roo.EventObject} e
9299          */
9300         "rowcontextmenu" : true,
9301         /**
9302          * @event cellcontextmenu
9303          * Fires when a cell is right clicked
9304          * @param {Roo.bootstrap.Table} this
9305          * @param {Number} rowIndex
9306          * @param {Number} cellIndex
9307          * @param {Roo.EventObject} e
9308          */
9309          "cellcontextmenu" : true,
9310          /**
9311          * @event headercontextmenu
9312          * Fires when a header is right clicked
9313          * @param {Roo.bootstrap.Table} this
9314          * @param {Number} columnIndex
9315          * @param {Roo.EventObject} e
9316          */
9317         "headercontextmenu" : true,
9318         /**
9319          * @event mousedown
9320          * The raw mousedown event for the entire grid.
9321          * @param {Roo.EventObject} e
9322          */
9323         "mousedown" : true
9324         
9325     });
9326 };
9327
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9329     
9330     cls: false,
9331     
9332     empty_results : '',
9333     striped : false,
9334     scrollBody : false,
9335     bordered: false,
9336     hover:  false,
9337     condensed : false,
9338     responsive : false,
9339     sm : false,
9340     cm : false,
9341     store : false,
9342     loadMask : false,
9343     footerShow : true,
9344     headerShow : true,
9345     enableColumnResize: true,
9346     disableAutoSize: false,
9347   
9348     rowSelection : false,
9349     cellSelection : false,
9350     layout : false,
9351
9352     minColumnWidth : 50,
9353     
9354     // Roo.Element - the tbody
9355     bodyEl: false,  // <tbody> Roo.Element - thead element    
9356     headEl: false,  // <thead> Roo.Element - thead element
9357     resizeProxy : false, // proxy element for dragging?
9358
9359
9360     
9361     container: false, // used by gridpanel...
9362     
9363     lazyLoad : false,
9364     
9365     CSS : Roo.util.CSS,
9366     
9367     auto_hide_footer : false,
9368     
9369     view: false, // actually points to this..
9370     
9371     getAutoCreate : function()
9372     {
9373         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9374         
9375         cfg = {
9376             tag: 'table',
9377             cls : 'table', 
9378             cn : []
9379         };
9380         // this get's auto added by panel.Grid
9381         if (this.scrollBody) {
9382             cfg.cls += ' table-body-fixed';
9383         }    
9384         if (this.striped) {
9385             cfg.cls += ' table-striped';
9386         }
9387         
9388         if (this.hover) {
9389             cfg.cls += ' table-hover';
9390         }
9391         if (this.bordered) {
9392             cfg.cls += ' table-bordered';
9393         }
9394         if (this.condensed) {
9395             cfg.cls += ' table-condensed';
9396         }
9397         
9398         if (this.responsive) {
9399             cfg.cls += ' table-responsive';
9400         }
9401         
9402         if (this.cls) {
9403             cfg.cls+=  ' ' +this.cls;
9404         }
9405         
9406         
9407         
9408         if (this.layout) {
9409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9410         }
9411         
9412         if(this.store || this.cm){
9413             if(this.headerShow){
9414                 cfg.cn.push(this.renderHeader());
9415             }
9416             
9417             cfg.cn.push(this.renderBody());
9418             
9419             if(this.footerShow){
9420                 cfg.cn.push(this.renderFooter());
9421             }
9422             // where does this come from?
9423             //cfg.cls+=  ' TableGrid';
9424         }
9425         
9426         return { cn : [ cfg ] };
9427     },
9428     
9429     initEvents : function()
9430     {   
9431         if(!this.store || !this.cm){
9432             return;
9433         }
9434         if (this.selModel) {
9435             this.selModel.initEvents();
9436         }
9437         
9438         
9439         //Roo.log('initEvents with ds!!!!');
9440         
9441         this.bodyEl = this.el.select('tbody', true).first();
9442         this.headEl = this.el.select('thead', true).first();
9443         this.mainFoot = this.el.select('tfoot', true).first();
9444         
9445         
9446         
9447         
9448         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449             e.on('click', this.sort, this);
9450         }, this);
9451         
9452         
9453         // why is this done????? = it breaks dialogs??
9454         //this.parent().el.setStyle('position', 'relative');
9455         
9456         
9457         if (this.footer) {
9458             this.footer.parentId = this.id;
9459             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9460             
9461             if(this.lazyLoad){
9462                 this.el.select('tfoot tr td').first().addClass('hide');
9463             }
9464         } 
9465         
9466         if(this.loadMask) {
9467             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9468         }
9469         
9470         this.store.on('load', this.onLoad, this);
9471         this.store.on('beforeload', this.onBeforeLoad, this);
9472         this.store.on('update', this.onUpdate, this);
9473         this.store.on('add', this.onAdd, this);
9474         this.store.on("clear", this.clear, this);
9475         
9476         this.el.on("contextmenu", this.onContextMenu, this);
9477         
9478         
9479         this.cm.on("headerchange", this.onHeaderChange, this);
9480         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481
9482  //?? does bodyEl get replaced on render?
9483         this.bodyEl.on("click", this.onClick, this);
9484         this.bodyEl.on("dblclick", this.onDblClick, this);        
9485         this.bodyEl.on('scroll', this.onBodyScroll, this);
9486
9487         // guessing mainbody will work - this relays usually caught by selmodel at present.
9488         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9489   
9490   
9491         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9492         
9493   
9494         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496         }
9497         
9498         this.initCSS();
9499     },
9500     // Compatibility with grid - we implement all the view features at present.
9501     getView : function()
9502     {
9503         return this;
9504     },
9505     
9506     initCSS : function()
9507     {
9508         if(this.disableAutoSize) {
9509             return;
9510         }
9511         
9512         var cm = this.cm, styles = [];
9513         this.CSS.removeStyleSheet(this.id + '-cssrules');
9514         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9515         // we can honour xs/sm/md/xl  as widths...
9516         // we first have to decide what widht we are currently at...
9517         var sz = Roo.getGridSize();
9518         
9519         var total = 0;
9520         var last = -1;
9521         var cols = []; // visable cols.
9522         var total_abs = 0;
9523         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9524             var w = cm.getColumnWidth(i, false);
9525             if(cm.isHidden(i)){
9526                 cols.push( { rel : false, abs : 0 });
9527                 continue;
9528             }
9529             if (w !== false) {
9530                 cols.push( { rel : false, abs : w });
9531                 total_abs += w;
9532                 last = i; // not really..
9533                 continue;
9534             }
9535             var w = cm.getColumnWidth(i, sz);
9536             if (w > 0) {
9537                 last = i
9538             }
9539             total += w;
9540             cols.push( { rel : w, abs : false });
9541         }
9542         
9543         var avail = this.bodyEl.dom.clientWidth - total_abs;
9544         
9545         var unitWidth = Math.floor(avail / total);
9546         var rem = avail - (unitWidth * total);
9547         
9548         var hidden, width, pos = 0 , splithide , left;
9549         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9550             
9551             hidden = 'display:none;';
9552             left = '';
9553             width  = 'width:0px;';
9554             splithide = '';
9555             if(!cm.isHidden(i)){
9556                 hidden = '';
9557                 
9558                 
9559                 // we can honour xs/sm/md/xl ?
9560                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9561                 if (w===0) {
9562                     hidden = 'display:none;';
9563                 }
9564                 // width should return a small number...
9565                 if (i == last) {
9566                     w+=rem; // add the remaining with..
9567                 }
9568                 pos += w;
9569                 left = "left:" + (pos -4) + "px;";
9570                 width = "width:" + w+ "px;";
9571                 
9572             }
9573             if (this.responsive) {
9574                 width = '';
9575                 left = '';
9576                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9577                 splithide = 'display: none;';
9578             }
9579             
9580             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9581             if (this.headEl) {
9582                 if (i == last) {
9583                     splithide = 'display:none;';
9584                 }
9585                 
9586                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9587                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9588                             // this is the popover version..
9589                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9590                 );
9591             }
9592             
9593         }
9594         //Roo.log(styles.join(''));
9595         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9596         
9597     },
9598     
9599     
9600     
9601     onContextMenu : function(e, t)
9602     {
9603         this.processEvent("contextmenu", e);
9604     },
9605     
9606     processEvent : function(name, e)
9607     {
9608         if (name != 'touchstart' ) {
9609             this.fireEvent(name, e);    
9610         }
9611         
9612         var t = e.getTarget();
9613         
9614         var cell = Roo.get(t);
9615         
9616         if(!cell){
9617             return;
9618         }
9619         
9620         if(cell.findParent('tfoot', false, true)){
9621             return;
9622         }
9623         
9624         if(cell.findParent('thead', false, true)){
9625             
9626             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9627                 cell = Roo.get(t).findParent('th', false, true);
9628                 if (!cell) {
9629                     Roo.log("failed to find th in thead?");
9630                     Roo.log(e.getTarget());
9631                     return;
9632                 }
9633             }
9634             
9635             var cellIndex = cell.dom.cellIndex;
9636             
9637             var ename = name == 'touchstart' ? 'click' : name;
9638             this.fireEvent("header" + ename, this, cellIndex, e);
9639             
9640             return;
9641         }
9642         
9643         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9644             cell = Roo.get(t).findParent('td', false, true);
9645             if (!cell) {
9646                 Roo.log("failed to find th in tbody?");
9647                 Roo.log(e.getTarget());
9648                 return;
9649             }
9650         }
9651         
9652         var row = cell.findParent('tr', false, true);
9653         var cellIndex = cell.dom.cellIndex;
9654         var rowIndex = row.dom.rowIndex - 1;
9655         
9656         if(row !== false){
9657             
9658             this.fireEvent("row" + name, this, rowIndex, e);
9659             
9660             if(cell !== false){
9661             
9662                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9663             }
9664         }
9665         
9666     },
9667     
9668     onMouseover : function(e, el)
9669     {
9670         var cell = Roo.get(el);
9671         
9672         if(!cell){
9673             return;
9674         }
9675         
9676         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9677             cell = cell.findParent('td', false, true);
9678         }
9679         
9680         var row = cell.findParent('tr', false, true);
9681         var cellIndex = cell.dom.cellIndex;
9682         var rowIndex = row.dom.rowIndex - 1; // start from 0
9683         
9684         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9685         
9686     },
9687     
9688     onMouseout : function(e, el)
9689     {
9690         var cell = Roo.get(el);
9691         
9692         if(!cell){
9693             return;
9694         }
9695         
9696         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697             cell = cell.findParent('td', false, true);
9698         }
9699         
9700         var row = cell.findParent('tr', false, true);
9701         var cellIndex = cell.dom.cellIndex;
9702         var rowIndex = row.dom.rowIndex - 1; // start from 0
9703         
9704         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9705         
9706     },
9707     
9708     onClick : function(e, el)
9709     {
9710         var cell = Roo.get(el);
9711         
9712         if(!cell || (!this.cellSelection && !this.rowSelection)){
9713             return;
9714         }
9715         
9716         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717             cell = cell.findParent('td', false, true);
9718         }
9719         
9720         if(!cell || typeof(cell) == 'undefined'){
9721             return;
9722         }
9723         
9724         var row = cell.findParent('tr', false, true);
9725         
9726         if(!row || typeof(row) == 'undefined'){
9727             return;
9728         }
9729         
9730         var cellIndex = cell.dom.cellIndex;
9731         var rowIndex = this.getRowIndex(row);
9732         
9733         // why??? - should these not be based on SelectionModel?
9734         //if(this.cellSelection){
9735             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9736         //}
9737         
9738         //if(this.rowSelection){
9739             this.fireEvent('rowclick', this, row, rowIndex, e);
9740         //}
9741          
9742     },
9743         
9744     onDblClick : function(e,el)
9745     {
9746         var cell = Roo.get(el);
9747         
9748         if(!cell || (!this.cellSelection && !this.rowSelection)){
9749             return;
9750         }
9751         
9752         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9753             cell = cell.findParent('td', false, true);
9754         }
9755         
9756         if(!cell || typeof(cell) == 'undefined'){
9757             return;
9758         }
9759         
9760         var row = cell.findParent('tr', false, true);
9761         
9762         if(!row || typeof(row) == 'undefined'){
9763             return;
9764         }
9765         
9766         var cellIndex = cell.dom.cellIndex;
9767         var rowIndex = this.getRowIndex(row);
9768         
9769         if(this.cellSelection){
9770             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9771         }
9772         
9773         if(this.rowSelection){
9774             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9775         }
9776     },
9777     findRowIndex : function(el)
9778     {
9779         var cell = Roo.get(el);
9780         if(!cell) {
9781             return false;
9782         }
9783         var row = cell.findParent('tr', false, true);
9784         
9785         if(!row || typeof(row) == 'undefined'){
9786             return false;
9787         }
9788         return this.getRowIndex(row);
9789     },
9790     sort : function(e,el)
9791     {
9792         var col = Roo.get(el);
9793         
9794         if(!col.hasClass('sortable')){
9795             return;
9796         }
9797         
9798         var sort = col.attr('sort');
9799         var dir = 'ASC';
9800         
9801         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9802             dir = 'DESC';
9803         }
9804         
9805         this.store.sortInfo = {field : sort, direction : dir};
9806         
9807         if (this.footer) {
9808             Roo.log("calling footer first");
9809             this.footer.onClick('first');
9810         } else {
9811         
9812             this.store.load({ params : { start : 0 } });
9813         }
9814     },
9815     
9816     renderHeader : function()
9817     {
9818         var header = {
9819             tag: 'thead',
9820             cn : []
9821         };
9822         
9823         var cm = this.cm;
9824         this.totalWidth = 0;
9825         
9826         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9827             
9828             var config = cm.config[i];
9829             
9830             var c = {
9831                 tag: 'th',
9832                 cls : 'x-hcol-' + i,
9833                 style : '',
9834                 
9835                 html: cm.getColumnHeader(i)
9836             };
9837             
9838             var tooltip = cm.getColumnTooltip(i);
9839             if (tooltip) {
9840                 c.tooltip = tooltip;
9841             }
9842             
9843             
9844             var hh = '';
9845             
9846             if(typeof(config.sortable) != 'undefined' && config.sortable){
9847                 c.cls += ' sortable';
9848                 c.html = '<i class="fa"></i>' + c.html;
9849             }
9850             
9851             // could use BS4 hidden-..-down 
9852             
9853             if(typeof(config.lgHeader) != 'undefined'){
9854                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9855             }
9856             
9857             if(typeof(config.mdHeader) != 'undefined'){
9858                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9859             }
9860             
9861             if(typeof(config.smHeader) != 'undefined'){
9862                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9863             }
9864             
9865             if(typeof(config.xsHeader) != 'undefined'){
9866                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9867             }
9868             
9869             if(hh.length){
9870                 c.html = hh;
9871             }
9872             
9873             if(typeof(config.tooltip) != 'undefined'){
9874                 c.tooltip = config.tooltip;
9875             }
9876             
9877             if(typeof(config.colspan) != 'undefined'){
9878                 c.colspan = config.colspan;
9879             }
9880             
9881             // hidden is handled by CSS now
9882             
9883             if(typeof(config.dataIndex) != 'undefined'){
9884                 c.sort = config.dataIndex;
9885             }
9886             
9887            
9888             
9889             if(typeof(config.align) != 'undefined' && config.align.length){
9890                 c.style += ' text-align:' + config.align + ';';
9891             }
9892             
9893             /* width is done in CSS
9894              *if(typeof(config.width) != 'undefined'){
9895                 c.style += ' width:' + config.width + 'px;';
9896                 this.totalWidth += config.width;
9897             } else {
9898                 this.totalWidth += 100; // assume minimum of 100 per column?
9899             }
9900             */
9901             
9902             if(typeof(config.cls) != 'undefined'){
9903                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9904             }
9905             // this is the bit that doesnt reall work at all...
9906             
9907             if (this.responsive) {
9908                  
9909             
9910                 ['xs','sm','md','lg'].map(function(size){
9911                     
9912                     if(typeof(config[size]) == 'undefined'){
9913                         return;
9914                     }
9915                      
9916                     if (!config[size]) { // 0 = hidden
9917                         // BS 4 '0' is treated as hide that column and below.
9918                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9919                         return;
9920                     }
9921                     
9922                     c.cls += ' col-' + size + '-' + config[size] + (
9923                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9924                     );
9925                     
9926                     
9927                 });
9928             }
9929             // at the end?
9930             
9931             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9932             
9933             
9934             
9935             
9936             header.cn.push(c)
9937         }
9938         
9939         return header;
9940     },
9941     
9942     renderBody : function()
9943     {
9944         var body = {
9945             tag: 'tbody',
9946             cn : [
9947                 {
9948                     tag: 'tr',
9949                     cn : [
9950                         {
9951                             tag : 'td',
9952                             colspan :  this.cm.getColumnCount()
9953                         }
9954                     ]
9955                 }
9956             ]
9957         };
9958         
9959         return body;
9960     },
9961     
9962     renderFooter : function()
9963     {
9964         var footer = {
9965             tag: 'tfoot',
9966             cn : [
9967                 {
9968                     tag: 'tr',
9969                     cn : [
9970                         {
9971                             tag : 'td',
9972                             colspan :  this.cm.getColumnCount()
9973                         }
9974                     ]
9975                 }
9976             ]
9977         };
9978         
9979         return footer;
9980     },
9981     
9982     
9983     
9984     onLoad : function()
9985     {
9986 //        Roo.log('ds onload');
9987         this.clear();
9988         
9989         var _this = this;
9990         var cm = this.cm;
9991         var ds = this.store;
9992         
9993         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9994             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9995             if (_this.store.sortInfo) {
9996                     
9997                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9998                     e.select('i', true).addClass(['fa-arrow-up']);
9999                 }
10000                 
10001                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10002                     e.select('i', true).addClass(['fa-arrow-down']);
10003                 }
10004             }
10005         });
10006         
10007         var tbody =  this.bodyEl;
10008               
10009         if(ds.getCount() > 0){
10010             ds.data.each(function(d,rowIndex){
10011                 var row =  this.renderRow(cm, ds, rowIndex);
10012                 
10013                 tbody.createChild(row);
10014                 
10015                 var _this = this;
10016                 
10017                 if(row.cellObjects.length){
10018                     Roo.each(row.cellObjects, function(r){
10019                         _this.renderCellObject(r);
10020                     })
10021                 }
10022                 
10023             }, this);
10024         } else if (this.empty_results.length) {
10025             this.el.mask(this.empty_results, 'no-spinner');
10026         }
10027         
10028         var tfoot = this.el.select('tfoot', true).first();
10029         
10030         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10031             
10032             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10033             
10034             var total = this.ds.getTotalCount();
10035             
10036             if(this.footer.pageSize < total){
10037                 this.mainFoot.show();
10038             }
10039         }
10040         
10041         Roo.each(this.el.select('tbody td', true).elements, function(e){
10042             e.on('mouseover', _this.onMouseover, _this);
10043         });
10044         
10045         Roo.each(this.el.select('tbody td', true).elements, function(e){
10046             e.on('mouseout', _this.onMouseout, _this);
10047         });
10048         this.fireEvent('rowsrendered', this);
10049         
10050         this.autoSize();
10051         
10052         this.initCSS(); /// resize cols
10053
10054         
10055     },
10056     
10057     
10058     onUpdate : function(ds,record)
10059     {
10060         this.refreshRow(record);
10061         this.autoSize();
10062     },
10063     
10064     onRemove : function(ds, record, index, isUpdate){
10065         if(isUpdate !== true){
10066             this.fireEvent("beforerowremoved", this, index, record);
10067         }
10068         var bt = this.bodyEl.dom;
10069         
10070         var rows = this.el.select('tbody > tr', true).elements;
10071         
10072         if(typeof(rows[index]) != 'undefined'){
10073             bt.removeChild(rows[index].dom);
10074         }
10075         
10076 //        if(bt.rows[index]){
10077 //            bt.removeChild(bt.rows[index]);
10078 //        }
10079         
10080         if(isUpdate !== true){
10081             //this.stripeRows(index);
10082             //this.syncRowHeights(index, index);
10083             //this.layout();
10084             this.fireEvent("rowremoved", this, index, record);
10085         }
10086     },
10087     
10088     onAdd : function(ds, records, rowIndex)
10089     {
10090         //Roo.log('on Add called');
10091         // - note this does not handle multiple adding very well..
10092         var bt = this.bodyEl.dom;
10093         for (var i =0 ; i < records.length;i++) {
10094             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10095             //Roo.log(records[i]);
10096             //Roo.log(this.store.getAt(rowIndex+i));
10097             this.insertRow(this.store, rowIndex + i, false);
10098             return;
10099         }
10100         
10101     },
10102     
10103     
10104     refreshRow : function(record){
10105         var ds = this.store, index;
10106         if(typeof record == 'number'){
10107             index = record;
10108             record = ds.getAt(index);
10109         }else{
10110             index = ds.indexOf(record);
10111             if (index < 0) {
10112                 return; // should not happen - but seems to 
10113             }
10114         }
10115         this.insertRow(ds, index, true);
10116         this.autoSize();
10117         this.onRemove(ds, record, index+1, true);
10118         this.autoSize();
10119         //this.syncRowHeights(index, index);
10120         //this.layout();
10121         this.fireEvent("rowupdated", this, index, record);
10122     },
10123     // private - called by RowSelection
10124     onRowSelect : function(rowIndex){
10125         var row = this.getRowDom(rowIndex);
10126         row.addClass(['bg-info','info']);
10127     },
10128     // private - called by RowSelection
10129     onRowDeselect : function(rowIndex)
10130     {
10131         if (rowIndex < 0) {
10132             return;
10133         }
10134         var row = this.getRowDom(rowIndex);
10135         row.removeClass(['bg-info','info']);
10136     },
10137       /**
10138      * Focuses the specified row.
10139      * @param {Number} row The row index
10140      */
10141     focusRow : function(row)
10142     {
10143         //Roo.log('GridView.focusRow');
10144         var x = this.bodyEl.dom.scrollLeft;
10145         this.focusCell(row, 0, false);
10146         this.bodyEl.dom.scrollLeft = x;
10147
10148     },
10149      /**
10150      * Focuses the specified cell.
10151      * @param {Number} row The row index
10152      * @param {Number} col The column index
10153      * @param {Boolean} hscroll false to disable horizontal scrolling
10154      */
10155     focusCell : function(row, col, hscroll)
10156     {
10157         //Roo.log('GridView.focusCell');
10158         var el = this.ensureVisible(row, col, hscroll);
10159         // not sure what focusEL achives = it's a <a> pos relative 
10160         //this.focusEl.alignTo(el, "tl-tl");
10161         //if(Roo.isGecko){
10162         //    this.focusEl.focus();
10163         //}else{
10164         //    this.focusEl.focus.defer(1, this.focusEl);
10165         //}
10166     },
10167     
10168      /**
10169      * Scrolls the specified cell into view
10170      * @param {Number} row The row index
10171      * @param {Number} col The column index
10172      * @param {Boolean} hscroll false to disable horizontal scrolling
10173      */
10174     ensureVisible : function(row, col, hscroll)
10175     {
10176         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10177         //return null; //disable for testing.
10178         if(typeof row != "number"){
10179             row = row.rowIndex;
10180         }
10181         if(row < 0 && row >= this.ds.getCount()){
10182             return  null;
10183         }
10184         col = (col !== undefined ? col : 0);
10185         var cm = this.cm;
10186         while(cm.isHidden(col)){
10187             col++;
10188         }
10189
10190         var el = this.getCellDom(row, col);
10191         if(!el){
10192             return null;
10193         }
10194         var c = this.bodyEl.dom;
10195
10196         var ctop = parseInt(el.offsetTop, 10);
10197         var cleft = parseInt(el.offsetLeft, 10);
10198         var cbot = ctop + el.offsetHeight;
10199         var cright = cleft + el.offsetWidth;
10200
10201         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10202         var ch = 0; //?? header is not withing the area?
10203         var stop = parseInt(c.scrollTop, 10);
10204         var sleft = parseInt(c.scrollLeft, 10);
10205         var sbot = stop + ch;
10206         var sright = sleft + c.clientWidth;
10207         /*
10208         Roo.log('GridView.ensureVisible:' +
10209                 ' ctop:' + ctop +
10210                 ' c.clientHeight:' + c.clientHeight +
10211                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10212                 ' stop:' + stop +
10213                 ' cbot:' + cbot +
10214                 ' sbot:' + sbot +
10215                 ' ch:' + ch  
10216                 );
10217         */
10218         if(ctop < stop){
10219             c.scrollTop = ctop;
10220             //Roo.log("set scrolltop to ctop DISABLE?");
10221         }else if(cbot > sbot){
10222             //Roo.log("set scrolltop to cbot-ch");
10223             c.scrollTop = cbot-ch;
10224         }
10225
10226         if(hscroll !== false){
10227             if(cleft < sleft){
10228                 c.scrollLeft = cleft;
10229             }else if(cright > sright){
10230                 c.scrollLeft = cright-c.clientWidth;
10231             }
10232         }
10233
10234         return el;
10235     },
10236     
10237     
10238     insertRow : function(dm, rowIndex, isUpdate){
10239         
10240         if(!isUpdate){
10241             this.fireEvent("beforerowsinserted", this, rowIndex);
10242         }
10243             //var s = this.getScrollState();
10244         var row = this.renderRow(this.cm, this.store, rowIndex);
10245         // insert before rowIndex..
10246         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10247         
10248         var _this = this;
10249                 
10250         if(row.cellObjects.length){
10251             Roo.each(row.cellObjects, function(r){
10252                 _this.renderCellObject(r);
10253             })
10254         }
10255             
10256         if(!isUpdate){
10257             this.fireEvent("rowsinserted", this, rowIndex);
10258             //this.syncRowHeights(firstRow, lastRow);
10259             //this.stripeRows(firstRow);
10260             //this.layout();
10261         }
10262         
10263     },
10264     
10265     
10266     getRowDom : function(rowIndex)
10267     {
10268         var rows = this.el.select('tbody > tr', true).elements;
10269         
10270         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10271         
10272     },
10273     getCellDom : function(rowIndex, colIndex)
10274     {
10275         var row = this.getRowDom(rowIndex);
10276         if (row === false) {
10277             return false;
10278         }
10279         var cols = row.select('td', true).elements;
10280         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10281         
10282     },
10283     
10284     // returns the object tree for a tr..
10285   
10286     
10287     renderRow : function(cm, ds, rowIndex) 
10288     {
10289         var d = ds.getAt(rowIndex);
10290         
10291         var row = {
10292             tag : 'tr',
10293             cls : 'x-row-' + rowIndex,
10294             cn : []
10295         };
10296             
10297         var cellObjects = [];
10298         
10299         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10300             var config = cm.config[i];
10301             
10302             var renderer = cm.getRenderer(i);
10303             var value = '';
10304             var id = false;
10305             
10306             if(typeof(renderer) !== 'undefined'){
10307                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10308             }
10309             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10310             // and are rendered into the cells after the row is rendered - using the id for the element.
10311             
10312             if(typeof(value) === 'object'){
10313                 id = Roo.id();
10314                 cellObjects.push({
10315                     container : id,
10316                     cfg : value 
10317                 })
10318             }
10319             
10320             var rowcfg = {
10321                 record: d,
10322                 rowIndex : rowIndex,
10323                 colIndex : i,
10324                 rowClass : ''
10325             };
10326
10327             this.fireEvent('rowclass', this, rowcfg);
10328             
10329             var td = {
10330                 tag: 'td',
10331                 // this might end up displaying HTML?
10332                 // this is too messy... - better to only do it on columsn you know are going to be too long
10333                 //tooltip : (typeof(value) === 'object') ? '' : value,
10334                 cls : rowcfg.rowClass + ' x-col-' + i,
10335                 style: '',
10336                 html: (typeof(value) === 'object') ? '' : value
10337             };
10338             
10339             if (id) {
10340                 td.id = id;
10341             }
10342             
10343             if(typeof(config.colspan) != 'undefined'){
10344                 td.colspan = config.colspan;
10345             }
10346             
10347             
10348             
10349             if(typeof(config.align) != 'undefined' && config.align.length){
10350                 td.style += ' text-align:' + config.align + ';';
10351             }
10352             if(typeof(config.valign) != 'undefined' && config.valign.length){
10353                 td.style += ' vertical-align:' + config.valign + ';';
10354             }
10355             /*
10356             if(typeof(config.width) != 'undefined'){
10357                 td.style += ' width:' +  config.width + 'px;';
10358             }
10359             */
10360             
10361             if(typeof(config.cursor) != 'undefined'){
10362                 td.style += ' cursor:' +  config.cursor + ';';
10363             }
10364             
10365             if(typeof(config.cls) != 'undefined'){
10366                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10367             }
10368             if (this.responsive) {
10369                 ['xs','sm','md','lg'].map(function(size){
10370                     
10371                     if(typeof(config[size]) == 'undefined'){
10372                         return;
10373                     }
10374                     
10375                     
10376                       
10377                     if (!config[size]) { // 0 = hidden
10378                         // BS 4 '0' is treated as hide that column and below.
10379                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10380                         return;
10381                     }
10382                     
10383                     td.cls += ' col-' + size + '-' + config[size] + (
10384                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10385                     );
10386                      
10387     
10388                 });
10389             }
10390             row.cn.push(td);
10391            
10392         }
10393         
10394         row.cellObjects = cellObjects;
10395         
10396         return row;
10397           
10398     },
10399     
10400     
10401     
10402     onBeforeLoad : function()
10403     {
10404         this.el.unmask(); // if needed.
10405     },
10406      /**
10407      * Remove all rows
10408      */
10409     clear : function()
10410     {
10411         this.el.select('tbody', true).first().dom.innerHTML = '';
10412     },
10413     /**
10414      * Show or hide a row.
10415      * @param {Number} rowIndex to show or hide
10416      * @param {Boolean} state hide
10417      */
10418     setRowVisibility : function(rowIndex, state)
10419     {
10420         var bt = this.bodyEl.dom;
10421         
10422         var rows = this.el.select('tbody > tr', true).elements;
10423         
10424         if(typeof(rows[rowIndex]) == 'undefined'){
10425             return;
10426         }
10427         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10428         
10429     },
10430     
10431     
10432     getSelectionModel : function(){
10433         if(!this.selModel){
10434             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10435         }
10436         return this.selModel;
10437     },
10438     /*
10439      * Render the Roo.bootstrap object from renderder
10440      */
10441     renderCellObject : function(r)
10442     {
10443         var _this = this;
10444         
10445         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10446         
10447         var t = r.cfg.render(r.container);
10448         
10449         if(r.cfg.cn){
10450             Roo.each(r.cfg.cn, function(c){
10451                 var child = {
10452                     container: t.getChildContainer(),
10453                     cfg: c
10454                 };
10455                 _this.renderCellObject(child);
10456             })
10457         }
10458     },
10459     /**
10460      * get the Row Index from a dom element.
10461      * @param {Roo.Element} row The row to look for
10462      * @returns {Number} the row
10463      */
10464     getRowIndex : function(row)
10465     {
10466         var rowIndex = -1;
10467         
10468         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10469             if(el != row){
10470                 return;
10471             }
10472             
10473             rowIndex = index;
10474         });
10475         
10476         return rowIndex;
10477     },
10478     /**
10479      * get the header TH element for columnIndex
10480      * @param {Number} columnIndex
10481      * @returns {Roo.Element}
10482      */
10483     getHeaderIndex: function(colIndex)
10484     {
10485         var cols = this.headEl.select('th', true).elements;
10486         return cols[colIndex]; 
10487     },
10488     /**
10489      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10490      * @param {domElement} cell to look for
10491      * @returns {Number} the column
10492      */
10493     getCellIndex : function(cell)
10494     {
10495         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10496         if(id){
10497             return parseInt(id[1], 10);
10498         }
10499         return 0;
10500     },
10501      /**
10502      * Returns the grid's underlying element = used by panel.Grid
10503      * @return {Element} The element
10504      */
10505     getGridEl : function(){
10506         return this.el;
10507     },
10508      /**
10509      * Forces a resize - used by panel.Grid
10510      * @return {Element} The element
10511      */
10512     autoSize : function()
10513     {
10514         if(this.disableAutoSize) {
10515             return;
10516         }
10517         //var ctr = Roo.get(this.container.dom.parentElement);
10518         var ctr = Roo.get(this.el.dom);
10519         
10520         var thd = this.getGridEl().select('thead',true).first();
10521         var tbd = this.getGridEl().select('tbody', true).first();
10522         var tfd = this.getGridEl().select('tfoot', true).first();
10523         
10524         var cw = ctr.getWidth();
10525         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10526         
10527         if (tbd) {
10528             
10529             tbd.setWidth(ctr.getWidth());
10530             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10531             // this needs fixing for various usage - currently only hydra job advers I think..
10532             //tdb.setHeight(
10533             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10534             //); 
10535             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10536             cw -= barsize;
10537         }
10538         cw = Math.max(cw, this.totalWidth);
10539         this.getGridEl().select('tbody tr',true).setWidth(cw);
10540         this.initCSS();
10541         
10542         // resize 'expandable coloumn?
10543         
10544         return; // we doe not have a view in this design..
10545         
10546     },
10547     onBodyScroll: function()
10548     {
10549         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10550         if(this.headEl){
10551             this.headEl.setStyle({
10552                 'position' : 'relative',
10553                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10554             });
10555         }
10556         
10557         if(this.lazyLoad){
10558             
10559             var scrollHeight = this.bodyEl.dom.scrollHeight;
10560             
10561             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10562             
10563             var height = this.bodyEl.getHeight();
10564             
10565             if(scrollHeight - height == scrollTop) {
10566                 
10567                 var total = this.ds.getTotalCount();
10568                 
10569                 if(this.footer.cursor + this.footer.pageSize < total){
10570                     
10571                     this.footer.ds.load({
10572                         params : {
10573                             start : this.footer.cursor + this.footer.pageSize,
10574                             limit : this.footer.pageSize
10575                         },
10576                         add : true
10577                     });
10578                 }
10579             }
10580             
10581         }
10582     },
10583     onColumnSplitterMoved : function(i, diff)
10584     {
10585         this.userResized = true;
10586         
10587         var cm = this.colModel;
10588         
10589         var w = this.getHeaderIndex(i).getWidth() + diff;
10590         
10591         
10592         cm.setColumnWidth(i, w, true);
10593         this.initCSS();
10594         //var cid = cm.getColumnId(i); << not used in this version?
10595        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10596         
10597         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10598         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10599         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10600 */
10601         //this.updateSplitters();
10602         //this.layout(); << ??
10603         this.fireEvent("columnresize", i, w);
10604     },
10605     onHeaderChange : function()
10606     {
10607         var header = this.renderHeader();
10608         var table = this.el.select('table', true).first();
10609         
10610         this.headEl.remove();
10611         this.headEl = table.createChild(header, this.bodyEl, false);
10612         
10613         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10614             e.on('click', this.sort, this);
10615         }, this);
10616         
10617         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10618             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10619         }
10620         
10621     },
10622     
10623     onHiddenChange : function(colModel, colIndex, hidden)
10624     {
10625         /*
10626         this.cm.setHidden()
10627         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10628         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10629         
10630         this.CSS.updateRule(thSelector, "display", "");
10631         this.CSS.updateRule(tdSelector, "display", "");
10632         
10633         if(hidden){
10634             this.CSS.updateRule(thSelector, "display", "none");
10635             this.CSS.updateRule(tdSelector, "display", "none");
10636         }
10637         */
10638         // onload calls initCSS()
10639         this.onHeaderChange();
10640         this.onLoad();
10641     },
10642     
10643     setColumnWidth: function(col_index, width)
10644     {
10645         // width = "md-2 xs-2..."
10646         if(!this.colModel.config[col_index]) {
10647             return;
10648         }
10649         
10650         var w = width.split(" ");
10651         
10652         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10653         
10654         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10655         
10656         
10657         for(var j = 0; j < w.length; j++) {
10658             
10659             if(!w[j]) {
10660                 continue;
10661             }
10662             
10663             var size_cls = w[j].split("-");
10664             
10665             if(!Number.isInteger(size_cls[1] * 1)) {
10666                 continue;
10667             }
10668             
10669             if(!this.colModel.config[col_index][size_cls[0]]) {
10670                 continue;
10671             }
10672             
10673             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10674                 continue;
10675             }
10676             
10677             h_row[0].classList.replace(
10678                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10679                 "col-"+size_cls[0]+"-"+size_cls[1]
10680             );
10681             
10682             for(var i = 0; i < rows.length; i++) {
10683                 
10684                 var size_cls = w[j].split("-");
10685                 
10686                 if(!Number.isInteger(size_cls[1] * 1)) {
10687                     continue;
10688                 }
10689                 
10690                 if(!this.colModel.config[col_index][size_cls[0]]) {
10691                     continue;
10692                 }
10693                 
10694                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10695                     continue;
10696                 }
10697                 
10698                 rows[i].classList.replace(
10699                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10700                     "col-"+size_cls[0]+"-"+size_cls[1]
10701                 );
10702             }
10703             
10704             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10705         }
10706     }
10707 });
10708
10709 // currently only used to find the split on drag.. 
10710 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10711
10712 /**
10713  * @depricated
10714 */
10715 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10716 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10717 /*
10718  * - LGPL
10719  *
10720  * table cell
10721  * 
10722  */
10723
10724 /**
10725  * @class Roo.bootstrap.TableCell
10726  * @extends Roo.bootstrap.Component
10727  * @children Roo.bootstrap.Component
10728  * @parent Roo.bootstrap.TableRow
10729  * Bootstrap TableCell class
10730  * 
10731  * @cfg {String} html cell contain text
10732  * @cfg {String} cls cell class
10733  * @cfg {String} tag cell tag (td|th) default td
10734  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10735  * @cfg {String} align Aligns the content in a cell
10736  * @cfg {String} axis Categorizes cells
10737  * @cfg {String} bgcolor Specifies the background color of a cell
10738  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10739  * @cfg {Number} colspan Specifies the number of columns a cell should span
10740  * @cfg {String} headers Specifies one or more header cells a cell is related to
10741  * @cfg {Number} height Sets the height of a cell
10742  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10743  * @cfg {Number} rowspan Sets the number of rows a cell should span
10744  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10745  * @cfg {String} valign Vertical aligns the content in a cell
10746  * @cfg {Number} width Specifies the width of a cell
10747  * 
10748  * @constructor
10749  * Create a new TableCell
10750  * @param {Object} config The config object
10751  */
10752
10753 Roo.bootstrap.TableCell = function(config){
10754     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10755 };
10756
10757 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10758     
10759     html: false,
10760     cls: false,
10761     tag: false,
10762     abbr: false,
10763     align: false,
10764     axis: false,
10765     bgcolor: false,
10766     charoff: false,
10767     colspan: false,
10768     headers: false,
10769     height: false,
10770     nowrap: false,
10771     rowspan: false,
10772     scope: false,
10773     valign: false,
10774     width: false,
10775     
10776     
10777     getAutoCreate : function(){
10778         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10779         
10780         cfg = {
10781             tag: 'td'
10782         };
10783         
10784         if(this.tag){
10785             cfg.tag = this.tag;
10786         }
10787         
10788         if (this.html) {
10789             cfg.html=this.html
10790         }
10791         if (this.cls) {
10792             cfg.cls=this.cls
10793         }
10794         if (this.abbr) {
10795             cfg.abbr=this.abbr
10796         }
10797         if (this.align) {
10798             cfg.align=this.align
10799         }
10800         if (this.axis) {
10801             cfg.axis=this.axis
10802         }
10803         if (this.bgcolor) {
10804             cfg.bgcolor=this.bgcolor
10805         }
10806         if (this.charoff) {
10807             cfg.charoff=this.charoff
10808         }
10809         if (this.colspan) {
10810             cfg.colspan=this.colspan
10811         }
10812         if (this.headers) {
10813             cfg.headers=this.headers
10814         }
10815         if (this.height) {
10816             cfg.height=this.height
10817         }
10818         if (this.nowrap) {
10819             cfg.nowrap=this.nowrap
10820         }
10821         if (this.rowspan) {
10822             cfg.rowspan=this.rowspan
10823         }
10824         if (this.scope) {
10825             cfg.scope=this.scope
10826         }
10827         if (this.valign) {
10828             cfg.valign=this.valign
10829         }
10830         if (this.width) {
10831             cfg.width=this.width
10832         }
10833         
10834         
10835         return cfg;
10836     }
10837    
10838 });
10839
10840  
10841
10842  /*
10843  * - LGPL
10844  *
10845  * table row
10846  * 
10847  */
10848
10849 /**
10850  * @class Roo.bootstrap.TableRow
10851  * @extends Roo.bootstrap.Component
10852  * @children Roo.bootstrap.TableCell
10853  * @parent Roo.bootstrap.TableBody
10854  * Bootstrap TableRow class
10855  * @cfg {String} cls row class
10856  * @cfg {String} align Aligns the content in a table row
10857  * @cfg {String} bgcolor Specifies a background color for a table row
10858  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10859  * @cfg {String} valign Vertical aligns the content in a table row
10860  * 
10861  * @constructor
10862  * Create a new TableRow
10863  * @param {Object} config The config object
10864  */
10865
10866 Roo.bootstrap.TableRow = function(config){
10867     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10868 };
10869
10870 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10871     
10872     cls: false,
10873     align: false,
10874     bgcolor: false,
10875     charoff: false,
10876     valign: false,
10877     
10878     getAutoCreate : function(){
10879         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10880         
10881         cfg = {
10882             tag: 'tr'
10883         };
10884             
10885         if(this.cls){
10886             cfg.cls = this.cls;
10887         }
10888         if(this.align){
10889             cfg.align = this.align;
10890         }
10891         if(this.bgcolor){
10892             cfg.bgcolor = this.bgcolor;
10893         }
10894         if(this.charoff){
10895             cfg.charoff = this.charoff;
10896         }
10897         if(this.valign){
10898             cfg.valign = this.valign;
10899         }
10900         
10901         return cfg;
10902     }
10903    
10904 });
10905
10906  
10907
10908  /*
10909  * - LGPL
10910  *
10911  * table body
10912  * 
10913  */
10914
10915 /**
10916  * @class Roo.bootstrap.TableBody
10917  * @extends Roo.bootstrap.Component
10918  * @children Roo.bootstrap.TableRow
10919  * @parent Roo.bootstrap.Table
10920  * Bootstrap TableBody class
10921  * @cfg {String} cls element class
10922  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10923  * @cfg {String} align Aligns the content inside the element
10924  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10925  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10926  * 
10927  * @constructor
10928  * Create a new TableBody
10929  * @param {Object} config The config object
10930  */
10931
10932 Roo.bootstrap.TableBody = function(config){
10933     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10934 };
10935
10936 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10937     
10938     cls: false,
10939     tag: false,
10940     align: false,
10941     charoff: false,
10942     valign: false,
10943     
10944     getAutoCreate : function(){
10945         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10946         
10947         cfg = {
10948             tag: 'tbody'
10949         };
10950             
10951         if (this.cls) {
10952             cfg.cls=this.cls
10953         }
10954         if(this.tag){
10955             cfg.tag = this.tag;
10956         }
10957         
10958         if(this.align){
10959             cfg.align = this.align;
10960         }
10961         if(this.charoff){
10962             cfg.charoff = this.charoff;
10963         }
10964         if(this.valign){
10965             cfg.valign = this.valign;
10966         }
10967         
10968         return cfg;
10969     }
10970     
10971     
10972 //    initEvents : function()
10973 //    {
10974 //        
10975 //        if(!this.store){
10976 //            return;
10977 //        }
10978 //        
10979 //        this.store = Roo.factory(this.store, Roo.data);
10980 //        this.store.on('load', this.onLoad, this);
10981 //        
10982 //        this.store.load();
10983 //        
10984 //    },
10985 //    
10986 //    onLoad: function () 
10987 //    {   
10988 //        this.fireEvent('load', this);
10989 //    }
10990 //    
10991 //   
10992 });
10993
10994  
10995
10996  /*
10997  * Based on:
10998  * Ext JS Library 1.1.1
10999  * Copyright(c) 2006-2007, Ext JS, LLC.
11000  *
11001  * Originally Released Under LGPL - original licence link has changed is not relivant.
11002  *
11003  * Fork - LGPL
11004  * <script type="text/javascript">
11005  */
11006
11007 // as we use this in bootstrap.
11008 Roo.namespace('Roo.form');
11009  /**
11010  * @class Roo.form.Action
11011  * Internal Class used to handle form actions
11012  * @constructor
11013  * @param {Roo.form.BasicForm} el The form element or its id
11014  * @param {Object} config Configuration options
11015  */
11016
11017  
11018  
11019 // define the action interface
11020 Roo.form.Action = function(form, options){
11021     this.form = form;
11022     this.options = options || {};
11023 };
11024 /**
11025  * Client Validation Failed
11026  * @const 
11027  */
11028 Roo.form.Action.CLIENT_INVALID = 'client';
11029 /**
11030  * Server Validation Failed
11031  * @const 
11032  */
11033 Roo.form.Action.SERVER_INVALID = 'server';
11034  /**
11035  * Connect to Server Failed
11036  * @const 
11037  */
11038 Roo.form.Action.CONNECT_FAILURE = 'connect';
11039 /**
11040  * Reading Data from Server Failed
11041  * @const 
11042  */
11043 Roo.form.Action.LOAD_FAILURE = 'load';
11044
11045 Roo.form.Action.prototype = {
11046     type : 'default',
11047     failureType : undefined,
11048     response : undefined,
11049     result : undefined,
11050
11051     // interface method
11052     run : function(options){
11053
11054     },
11055
11056     // interface method
11057     success : function(response){
11058
11059     },
11060
11061     // interface method
11062     handleResponse : function(response){
11063
11064     },
11065
11066     // default connection failure
11067     failure : function(response){
11068         
11069         this.response = response;
11070         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11071         this.form.afterAction(this, false);
11072     },
11073
11074     processResponse : function(response){
11075         this.response = response;
11076         if(!response.responseText){
11077             return true;
11078         }
11079         this.result = this.handleResponse(response);
11080         return this.result;
11081     },
11082
11083     // utility functions used internally
11084     getUrl : function(appendParams){
11085         var url = this.options.url || this.form.url || this.form.el.dom.action;
11086         if(appendParams){
11087             var p = this.getParams();
11088             if(p){
11089                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11090             }
11091         }
11092         return url;
11093     },
11094
11095     getMethod : function(){
11096         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11097     },
11098
11099     getParams : function(){
11100         var bp = this.form.baseParams;
11101         var p = this.options.params;
11102         if(p){
11103             if(typeof p == "object"){
11104                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11105             }else if(typeof p == 'string' && bp){
11106                 p += '&' + Roo.urlEncode(bp);
11107             }
11108         }else if(bp){
11109             p = Roo.urlEncode(bp);
11110         }
11111         return p;
11112     },
11113
11114     createCallback : function(){
11115         return {
11116             success: this.success,
11117             failure: this.failure,
11118             scope: this,
11119             timeout: (this.form.timeout*1000),
11120             upload: this.form.fileUpload ? this.success : undefined
11121         };
11122     }
11123 };
11124
11125 Roo.form.Action.Submit = function(form, options){
11126     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11127 };
11128
11129 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11130     type : 'submit',
11131
11132     haveProgress : false,
11133     uploadComplete : false,
11134     
11135     // uploadProgress indicator.
11136     uploadProgress : function()
11137     {
11138         if (!this.form.progressUrl) {
11139             return;
11140         }
11141         
11142         if (!this.haveProgress) {
11143             Roo.MessageBox.progress("Uploading", "Uploading");
11144         }
11145         if (this.uploadComplete) {
11146            Roo.MessageBox.hide();
11147            return;
11148         }
11149         
11150         this.haveProgress = true;
11151    
11152         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11153         
11154         var c = new Roo.data.Connection();
11155         c.request({
11156             url : this.form.progressUrl,
11157             params: {
11158                 id : uid
11159             },
11160             method: 'GET',
11161             success : function(req){
11162                //console.log(data);
11163                 var rdata = false;
11164                 var edata;
11165                 try  {
11166                    rdata = Roo.decode(req.responseText)
11167                 } catch (e) {
11168                     Roo.log("Invalid data from server..");
11169                     Roo.log(edata);
11170                     return;
11171                 }
11172                 if (!rdata || !rdata.success) {
11173                     Roo.log(rdata);
11174                     Roo.MessageBox.alert(Roo.encode(rdata));
11175                     return;
11176                 }
11177                 var data = rdata.data;
11178                 
11179                 if (this.uploadComplete) {
11180                    Roo.MessageBox.hide();
11181                    return;
11182                 }
11183                    
11184                 if (data){
11185                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11186                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11187                     );
11188                 }
11189                 this.uploadProgress.defer(2000,this);
11190             },
11191        
11192             failure: function(data) {
11193                 Roo.log('progress url failed ');
11194                 Roo.log(data);
11195             },
11196             scope : this
11197         });
11198            
11199     },
11200     
11201     
11202     run : function()
11203     {
11204         // run get Values on the form, so it syncs any secondary forms.
11205         this.form.getValues();
11206         
11207         var o = this.options;
11208         var method = this.getMethod();
11209         var isPost = method == 'POST';
11210         if(o.clientValidation === false || this.form.isValid()){
11211             
11212             if (this.form.progressUrl) {
11213                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11214                     (new Date() * 1) + '' + Math.random());
11215                     
11216             } 
11217             
11218             
11219             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11220                 form:this.form.el.dom,
11221                 url:this.getUrl(!isPost),
11222                 method: method,
11223                 params:isPost ? this.getParams() : null,
11224                 isUpload: this.form.fileUpload,
11225                 formData : this.form.formData
11226             }));
11227             
11228             this.uploadProgress();
11229
11230         }else if (o.clientValidation !== false){ // client validation failed
11231             this.failureType = Roo.form.Action.CLIENT_INVALID;
11232             this.form.afterAction(this, false);
11233         }
11234     },
11235
11236     success : function(response)
11237     {
11238         this.uploadComplete= true;
11239         if (this.haveProgress) {
11240             Roo.MessageBox.hide();
11241         }
11242         
11243         
11244         var result = this.processResponse(response);
11245         if(result === true || result.success){
11246             this.form.afterAction(this, true);
11247             return;
11248         }
11249         if(result.errors){
11250             this.form.markInvalid(result.errors);
11251             this.failureType = Roo.form.Action.SERVER_INVALID;
11252         }
11253         this.form.afterAction(this, false);
11254     },
11255     failure : function(response)
11256     {
11257         this.uploadComplete= true;
11258         if (this.haveProgress) {
11259             Roo.MessageBox.hide();
11260         }
11261         
11262         this.response = response;
11263         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11264         this.form.afterAction(this, false);
11265     },
11266     
11267     handleResponse : function(response){
11268         if(this.form.errorReader){
11269             var rs = this.form.errorReader.read(response);
11270             var errors = [];
11271             if(rs.records){
11272                 for(var i = 0, len = rs.records.length; i < len; i++) {
11273                     var r = rs.records[i];
11274                     errors[i] = r.data;
11275                 }
11276             }
11277             if(errors.length < 1){
11278                 errors = null;
11279             }
11280             return {
11281                 success : rs.success,
11282                 errors : errors
11283             };
11284         }
11285         var ret = false;
11286         try {
11287             ret = Roo.decode(response.responseText);
11288         } catch (e) {
11289             ret = {
11290                 success: false,
11291                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11292                 errors : []
11293             };
11294         }
11295         return ret;
11296         
11297     }
11298 });
11299
11300
11301 Roo.form.Action.Load = function(form, options){
11302     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11303     this.reader = this.form.reader;
11304 };
11305
11306 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11307     type : 'load',
11308
11309     run : function(){
11310         
11311         Roo.Ajax.request(Roo.apply(
11312                 this.createCallback(), {
11313                     method:this.getMethod(),
11314                     url:this.getUrl(false),
11315                     params:this.getParams()
11316         }));
11317     },
11318
11319     success : function(response){
11320         
11321         var result = this.processResponse(response);
11322         if(result === true || !result.success || !result.data){
11323             this.failureType = Roo.form.Action.LOAD_FAILURE;
11324             this.form.afterAction(this, false);
11325             return;
11326         }
11327         this.form.clearInvalid();
11328         this.form.setValues(result.data);
11329         this.form.afterAction(this, true);
11330     },
11331
11332     handleResponse : function(response){
11333         if(this.form.reader){
11334             var rs = this.form.reader.read(response);
11335             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11336             return {
11337                 success : rs.success,
11338                 data : data
11339             };
11340         }
11341         return Roo.decode(response.responseText);
11342     }
11343 });
11344
11345 Roo.form.Action.ACTION_TYPES = {
11346     'load' : Roo.form.Action.Load,
11347     'submit' : Roo.form.Action.Submit
11348 };/*
11349  * - LGPL
11350  *
11351  * form
11352  *
11353  */
11354
11355 /**
11356  * @class Roo.bootstrap.form.Form
11357  * @extends Roo.bootstrap.Component
11358  * @children Roo.bootstrap.Component
11359  * Bootstrap Form class
11360  * @cfg {String} method  GET | POST (default POST)
11361  * @cfg {String} labelAlign top | left (default top)
11362  * @cfg {String} align left  | right - for navbars
11363  * @cfg {Boolean} loadMask load mask when submit (default true)
11364
11365  *
11366  * @constructor
11367  * Create a new Form
11368  * @param {Object} config The config object
11369  */
11370
11371
11372 Roo.bootstrap.form.Form = function(config){
11373     
11374     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11375     
11376     Roo.bootstrap.form.Form.popover.apply();
11377     
11378     this.addEvents({
11379         /**
11380          * @event clientvalidation
11381          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11382          * @param {Form} this
11383          * @param {Boolean} valid true if the form has passed client-side validation
11384          */
11385         clientvalidation: true,
11386         /**
11387          * @event beforeaction
11388          * Fires before any action is performed. Return false to cancel the action.
11389          * @param {Form} this
11390          * @param {Action} action The action to be performed
11391          */
11392         beforeaction: true,
11393         /**
11394          * @event actionfailed
11395          * Fires when an action fails.
11396          * @param {Form} this
11397          * @param {Action} action The action that failed
11398          */
11399         actionfailed : true,
11400         /**
11401          * @event actioncomplete
11402          * Fires when an action is completed.
11403          * @param {Form} this
11404          * @param {Action} action The action that completed
11405          */
11406         actioncomplete : true
11407     });
11408 };
11409
11410 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11411
11412      /**
11413      * @cfg {String} method
11414      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11415      */
11416     method : 'POST',
11417     /**
11418      * @cfg {String} url
11419      * The URL to use for form actions if one isn't supplied in the action options.
11420      */
11421     /**
11422      * @cfg {Boolean} fileUpload
11423      * Set to true if this form is a file upload.
11424      */
11425
11426     /**
11427      * @cfg {Object} baseParams
11428      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11429      */
11430
11431     /**
11432      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11433      */
11434     timeout: 30,
11435     /**
11436      * @cfg {Sting} align (left|right) for navbar forms
11437      */
11438     align : 'left',
11439
11440     // private
11441     activeAction : null,
11442
11443     /**
11444      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11445      * element by passing it or its id or mask the form itself by passing in true.
11446      * @type Mixed
11447      */
11448     waitMsgTarget : false,
11449
11450     loadMask : true,
11451     
11452     /**
11453      * @cfg {Boolean} errorMask (true|false) default false
11454      */
11455     errorMask : false,
11456     
11457     /**
11458      * @cfg {Number} maskOffset Default 100
11459      */
11460     maskOffset : 100,
11461     
11462     /**
11463      * @cfg {Boolean} maskBody
11464      */
11465     maskBody : false,
11466
11467     getAutoCreate : function(){
11468
11469         var cfg = {
11470             tag: 'form',
11471             method : this.method || 'POST',
11472             id : this.id || Roo.id(),
11473             cls : ''
11474         };
11475         if (this.parent().xtype.match(/^Nav/)) {
11476             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11477
11478         }
11479
11480         if (this.labelAlign == 'left' ) {
11481             cfg.cls += ' form-horizontal';
11482         }
11483
11484
11485         return cfg;
11486     },
11487     initEvents : function()
11488     {
11489         this.el.on('submit', this.onSubmit, this);
11490         // this was added as random key presses on the form where triggering form submit.
11491         this.el.on('keypress', function(e) {
11492             if (e.getCharCode() != 13) {
11493                 return true;
11494             }
11495             // we might need to allow it for textareas.. and some other items.
11496             // check e.getTarget().
11497
11498             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11499                 return true;
11500             }
11501
11502             Roo.log("keypress blocked");
11503
11504             e.preventDefault();
11505             return false;
11506         });
11507         
11508     },
11509     // private
11510     onSubmit : function(e){
11511         e.stopEvent();
11512     },
11513
11514      /**
11515      * Returns true if client-side validation on the form is successful.
11516      * @return Boolean
11517      */
11518     isValid : function(){
11519         var items = this.getItems();
11520         var valid = true;
11521         var target = false;
11522         
11523         items.each(function(f){
11524             
11525             if(f.validate()){
11526                 return;
11527             }
11528             
11529             Roo.log('invalid field: ' + f.name);
11530             
11531             valid = false;
11532
11533             if(!target && f.el.isVisible(true)){
11534                 target = f;
11535             }
11536            
11537         });
11538         
11539         if(this.errorMask && !valid){
11540             Roo.bootstrap.form.Form.popover.mask(this, target);
11541         }
11542         
11543         return valid;
11544     },
11545     
11546     /**
11547      * Returns true if any fields in this form have changed since their original load.
11548      * @return Boolean
11549      */
11550     isDirty : function(){
11551         var dirty = false;
11552         var items = this.getItems();
11553         items.each(function(f){
11554            if(f.isDirty()){
11555                dirty = true;
11556                return false;
11557            }
11558            return true;
11559         });
11560         return dirty;
11561     },
11562      /**
11563      * Performs a predefined action (submit or load) or custom actions you define on this form.
11564      * @param {String} actionName The name of the action type
11565      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11566      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11567      * accept other config options):
11568      * <pre>
11569 Property          Type             Description
11570 ----------------  ---------------  ----------------------------------------------------------------------------------
11571 url               String           The url for the action (defaults to the form's url)
11572 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11573 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11574 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11575                                    validate the form on the client (defaults to false)
11576      * </pre>
11577      * @return {BasicForm} this
11578      */
11579     doAction : function(action, options){
11580         if(typeof action == 'string'){
11581             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11582         }
11583         if(this.fireEvent('beforeaction', this, action) !== false){
11584             this.beforeAction(action);
11585             action.run.defer(100, action);
11586         }
11587         return this;
11588     },
11589
11590     // private
11591     beforeAction : function(action){
11592         var o = action.options;
11593         
11594         if(this.loadMask){
11595             
11596             if(this.maskBody){
11597                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11598             } else {
11599                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11600             }
11601         }
11602         // not really supported yet.. ??
11603
11604         //if(this.waitMsgTarget === true){
11605         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11606         //}else if(this.waitMsgTarget){
11607         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11608         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11609         //}else {
11610         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11611        // }
11612
11613     },
11614
11615     // private
11616     afterAction : function(action, success){
11617         this.activeAction = null;
11618         var o = action.options;
11619
11620         if(this.loadMask){
11621             
11622             if(this.maskBody){
11623                 Roo.get(document.body).unmask();
11624             } else {
11625                 this.el.unmask();
11626             }
11627         }
11628         
11629         //if(this.waitMsgTarget === true){
11630 //            this.el.unmask();
11631         //}else if(this.waitMsgTarget){
11632         //    this.waitMsgTarget.unmask();
11633         //}else{
11634         //    Roo.MessageBox.updateProgress(1);
11635         //    Roo.MessageBox.hide();
11636        // }
11637         //
11638         if(success){
11639             if(o.reset){
11640                 this.reset();
11641             }
11642             Roo.callback(o.success, o.scope, [this, action]);
11643             this.fireEvent('actioncomplete', this, action);
11644
11645         }else{
11646
11647             // failure condition..
11648             // we have a scenario where updates need confirming.
11649             // eg. if a locking scenario exists..
11650             // we look for { errors : { needs_confirm : true }} in the response.
11651             if (
11652                 (typeof(action.result) != 'undefined')  &&
11653                 (typeof(action.result.errors) != 'undefined')  &&
11654                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11655            ){
11656                 var _t = this;
11657                 Roo.log("not supported yet");
11658                  /*
11659
11660                 Roo.MessageBox.confirm(
11661                     "Change requires confirmation",
11662                     action.result.errorMsg,
11663                     function(r) {
11664                         if (r != 'yes') {
11665                             return;
11666                         }
11667                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11668                     }
11669
11670                 );
11671                 */
11672
11673
11674                 return;
11675             }
11676
11677             Roo.callback(o.failure, o.scope, [this, action]);
11678             // show an error message if no failed handler is set..
11679             if (!this.hasListener('actionfailed')) {
11680                 Roo.log("need to add dialog support");
11681                 /*
11682                 Roo.MessageBox.alert("Error",
11683                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11684                         action.result.errorMsg :
11685                         "Saving Failed, please check your entries or try again"
11686                 );
11687                 */
11688             }
11689
11690             this.fireEvent('actionfailed', this, action);
11691         }
11692
11693     },
11694     /**
11695      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11696      * @param {String} id The value to search for
11697      * @return Field
11698      */
11699     findField : function(id){
11700         var items = this.getItems();
11701         var field = items.get(id);
11702         if(!field){
11703              items.each(function(f){
11704                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11705                     field = f;
11706                     return false;
11707                 }
11708                 return true;
11709             });
11710         }
11711         return field || null;
11712     },
11713      /**
11714      * Mark fields in this form invalid in bulk.
11715      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11716      * @return {BasicForm} this
11717      */
11718     markInvalid : function(errors){
11719         if(errors instanceof Array){
11720             for(var i = 0, len = errors.length; i < len; i++){
11721                 var fieldError = errors[i];
11722                 var f = this.findField(fieldError.id);
11723                 if(f){
11724                     f.markInvalid(fieldError.msg);
11725                 }
11726             }
11727         }else{
11728             var field, id;
11729             for(id in errors){
11730                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11731                     field.markInvalid(errors[id]);
11732                 }
11733             }
11734         }
11735         //Roo.each(this.childForms || [], function (f) {
11736         //    f.markInvalid(errors);
11737         //});
11738
11739         return this;
11740     },
11741
11742     /**
11743      * Set values for fields in this form in bulk.
11744      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11745      * @return {BasicForm} this
11746      */
11747     setValues : function(values){
11748         if(values instanceof Array){ // array of objects
11749             for(var i = 0, len = values.length; i < len; i++){
11750                 var v = values[i];
11751                 var f = this.findField(v.id);
11752                 if(f){
11753                     f.setValue(v.value);
11754                     if(this.trackResetOnLoad){
11755                         f.originalValue = f.getValue();
11756                     }
11757                 }
11758             }
11759         }else{ // object hash
11760             var field, id;
11761             for(id in values){
11762                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11763
11764                     if (field.setFromData &&
11765                         field.valueField &&
11766                         field.displayField &&
11767                         // combos' with local stores can
11768                         // be queried via setValue()
11769                         // to set their value..
11770                         (field.store && !field.store.isLocal)
11771                         ) {
11772                         // it's a combo
11773                         var sd = { };
11774                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11775                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11776                         field.setFromData(sd);
11777
11778                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11779                         
11780                         field.setFromData(values);
11781                         
11782                     } else {
11783                         field.setValue(values[id]);
11784                     }
11785
11786
11787                     if(this.trackResetOnLoad){
11788                         field.originalValue = field.getValue();
11789                     }
11790                 }
11791             }
11792         }
11793
11794         //Roo.each(this.childForms || [], function (f) {
11795         //    f.setValues(values);
11796         //});
11797
11798         return this;
11799     },
11800
11801     /**
11802      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11803      * they are returned as an array.
11804      * @param {Boolean} asString
11805      * @return {Object}
11806      */
11807     getValues : function(asString){
11808         //if (this.childForms) {
11809             // copy values from the child forms
11810         //    Roo.each(this.childForms, function (f) {
11811         //        this.setValues(f.getValues());
11812         //    }, this);
11813         //}
11814
11815
11816
11817         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11818         if(asString === true){
11819             return fs;
11820         }
11821         return Roo.urlDecode(fs);
11822     },
11823
11824     /**
11825      * Returns the fields in this form as an object with key/value pairs.
11826      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11827      * @return {Object}
11828      */
11829     getFieldValues : function(with_hidden)
11830     {
11831         var items = this.getItems();
11832         var ret = {};
11833         items.each(function(f){
11834             
11835             if (!f.getName()) {
11836                 return;
11837             }
11838             
11839             var v = f.getValue();
11840             
11841             if (f.inputType =='radio') {
11842                 if (typeof(ret[f.getName()]) == 'undefined') {
11843                     ret[f.getName()] = ''; // empty..
11844                 }
11845
11846                 if (!f.el.dom.checked) {
11847                     return;
11848
11849                 }
11850                 v = f.el.dom.value;
11851
11852             }
11853             
11854             if(f.xtype == 'MoneyField'){
11855                 ret[f.currencyName] = f.getCurrency();
11856             }
11857
11858             // not sure if this supported any more..
11859             if ((typeof(v) == 'object') && f.getRawValue) {
11860                 v = f.getRawValue() ; // dates..
11861             }
11862             // combo boxes where name != hiddenName...
11863             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11864                 ret[f.name] = f.getRawValue();
11865             }
11866             ret[f.getName()] = v;
11867         });
11868
11869         return ret;
11870     },
11871
11872     /**
11873      * Clears all invalid messages in this form.
11874      * @return {BasicForm} this
11875      */
11876     clearInvalid : function(){
11877         var items = this.getItems();
11878
11879         items.each(function(f){
11880            f.clearInvalid();
11881         });
11882
11883         return this;
11884     },
11885
11886     /**
11887      * Resets this form.
11888      * @return {BasicForm} this
11889      */
11890     reset : function(){
11891         var items = this.getItems();
11892         items.each(function(f){
11893             f.reset();
11894         });
11895
11896         Roo.each(this.childForms || [], function (f) {
11897             f.reset();
11898         });
11899
11900
11901         return this;
11902     },
11903     
11904     getItems : function()
11905     {
11906         var r=new Roo.util.MixedCollection(false, function(o){
11907             return o.id || (o.id = Roo.id());
11908         });
11909         var iter = function(el) {
11910             if (el.inputEl) {
11911                 r.add(el);
11912             }
11913             if (!el.items) {
11914                 return;
11915             }
11916             Roo.each(el.items,function(e) {
11917                 iter(e);
11918             });
11919         };
11920
11921         iter(this);
11922         return r;
11923     },
11924     
11925     hideFields : function(items)
11926     {
11927         Roo.each(items, function(i){
11928             
11929             var f = this.findField(i);
11930             
11931             if(!f){
11932                 return;
11933             }
11934             
11935             f.hide();
11936             
11937         }, this);
11938     },
11939     
11940     showFields : function(items)
11941     {
11942         Roo.each(items, function(i){
11943             
11944             var f = this.findField(i);
11945             
11946             if(!f){
11947                 return;
11948             }
11949             
11950             f.show();
11951             
11952         }, this);
11953     }
11954
11955 });
11956
11957 Roo.apply(Roo.bootstrap.form.Form, {
11958     
11959     popover : {
11960         
11961         padding : 5,
11962         
11963         isApplied : false,
11964         
11965         isMasked : false,
11966         
11967         form : false,
11968         
11969         target : false,
11970         
11971         toolTip : false,
11972         
11973         intervalID : false,
11974         
11975         maskEl : false,
11976         
11977         apply : function()
11978         {
11979             if(this.isApplied){
11980                 return;
11981             }
11982             
11983             this.maskEl = {
11984                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11985                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11986                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11987                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11988             };
11989             
11990             this.maskEl.top.enableDisplayMode("block");
11991             this.maskEl.left.enableDisplayMode("block");
11992             this.maskEl.bottom.enableDisplayMode("block");
11993             this.maskEl.right.enableDisplayMode("block");
11994             
11995             this.toolTip = new Roo.bootstrap.Tooltip({
11996                 cls : 'roo-form-error-popover',
11997                 alignment : {
11998                     'left' : ['r-l', [-2,0], 'right'],
11999                     'right' : ['l-r', [2,0], 'left'],
12000                     'bottom' : ['tl-bl', [0,2], 'top'],
12001                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12002                 }
12003             });
12004             
12005             this.toolTip.render(Roo.get(document.body));
12006
12007             this.toolTip.el.enableDisplayMode("block");
12008             
12009             Roo.get(document.body).on('click', function(){
12010                 this.unmask();
12011             }, this);
12012             
12013             Roo.get(document.body).on('touchstart', function(){
12014                 this.unmask();
12015             }, this);
12016             
12017             this.isApplied = true
12018         },
12019         
12020         mask : function(form, target)
12021         {
12022             this.form = form;
12023             
12024             this.target = target;
12025             
12026             if(!this.form.errorMask || !target.el){
12027                 return;
12028             }
12029             
12030             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12031             
12032             Roo.log(scrollable);
12033             
12034             var ot = this.target.el.calcOffsetsTo(scrollable);
12035             
12036             var scrollTo = ot[1] - this.form.maskOffset;
12037             
12038             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12039             
12040             scrollable.scrollTo('top', scrollTo);
12041             
12042             var box = this.target.el.getBox();
12043             Roo.log(box);
12044             var zIndex = Roo.bootstrap.Modal.zIndex++;
12045
12046             
12047             this.maskEl.top.setStyle('position', 'absolute');
12048             this.maskEl.top.setStyle('z-index', zIndex);
12049             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12050             this.maskEl.top.setLeft(0);
12051             this.maskEl.top.setTop(0);
12052             this.maskEl.top.show();
12053             
12054             this.maskEl.left.setStyle('position', 'absolute');
12055             this.maskEl.left.setStyle('z-index', zIndex);
12056             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12057             this.maskEl.left.setLeft(0);
12058             this.maskEl.left.setTop(box.y - this.padding);
12059             this.maskEl.left.show();
12060
12061             this.maskEl.bottom.setStyle('position', 'absolute');
12062             this.maskEl.bottom.setStyle('z-index', zIndex);
12063             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12064             this.maskEl.bottom.setLeft(0);
12065             this.maskEl.bottom.setTop(box.bottom + this.padding);
12066             this.maskEl.bottom.show();
12067
12068             this.maskEl.right.setStyle('position', 'absolute');
12069             this.maskEl.right.setStyle('z-index', zIndex);
12070             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12071             this.maskEl.right.setLeft(box.right + this.padding);
12072             this.maskEl.right.setTop(box.y - this.padding);
12073             this.maskEl.right.show();
12074
12075             this.toolTip.bindEl = this.target.el;
12076
12077             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12078
12079             var tip = this.target.blankText;
12080
12081             if(this.target.getValue() !== '' ) {
12082                 
12083                 if (this.target.invalidText.length) {
12084                     tip = this.target.invalidText;
12085                 } else if (this.target.regexText.length){
12086                     tip = this.target.regexText;
12087                 }
12088             }
12089
12090             this.toolTip.show(tip);
12091
12092             this.intervalID = window.setInterval(function() {
12093                 Roo.bootstrap.form.Form.popover.unmask();
12094             }, 10000);
12095
12096             window.onwheel = function(){ return false;};
12097             
12098             (function(){ this.isMasked = true; }).defer(500, this);
12099             
12100         },
12101         
12102         unmask : function()
12103         {
12104             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12105                 return;
12106             }
12107             
12108             this.maskEl.top.setStyle('position', 'absolute');
12109             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12110             this.maskEl.top.hide();
12111
12112             this.maskEl.left.setStyle('position', 'absolute');
12113             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12114             this.maskEl.left.hide();
12115
12116             this.maskEl.bottom.setStyle('position', 'absolute');
12117             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12118             this.maskEl.bottom.hide();
12119
12120             this.maskEl.right.setStyle('position', 'absolute');
12121             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12122             this.maskEl.right.hide();
12123             
12124             this.toolTip.hide();
12125             
12126             this.toolTip.el.hide();
12127             
12128             window.onwheel = function(){ return true;};
12129             
12130             if(this.intervalID){
12131                 window.clearInterval(this.intervalID);
12132                 this.intervalID = false;
12133             }
12134             
12135             this.isMasked = false;
12136             
12137         }
12138         
12139     }
12140     
12141 });
12142
12143 /*
12144  * Based on:
12145  * Ext JS Library 1.1.1
12146  * Copyright(c) 2006-2007, Ext JS, LLC.
12147  *
12148  * Originally Released Under LGPL - original licence link has changed is not relivant.
12149  *
12150  * Fork - LGPL
12151  * <script type="text/javascript">
12152  */
12153 /**
12154  * @class Roo.form.VTypes
12155  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12156  * @static
12157  */
12158 Roo.form.VTypes = function(){
12159     // closure these in so they are only created once.
12160     var alpha = /^[a-zA-Z_]+$/;
12161     var alphanum = /^[a-zA-Z0-9_]+$/;
12162     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12163     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12164
12165     // All these messages and functions are configurable
12166     return {
12167         /**
12168          * The function used to validate email addresses
12169          * @param {String} value The email address
12170          */
12171         'email' : function(v){
12172             return email.test(v);
12173         },
12174         /**
12175          * The error text to display when the email validation function returns false
12176          * @type String
12177          */
12178         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12179         /**
12180          * The keystroke filter mask to be applied on email input
12181          * @type RegExp
12182          */
12183         'emailMask' : /[a-z0-9_\.\-@]/i,
12184
12185         /**
12186          * The function used to validate URLs
12187          * @param {String} value The URL
12188          */
12189         'url' : function(v){
12190             return url.test(v);
12191         },
12192         /**
12193          * The error text to display when the url validation function returns false
12194          * @type String
12195          */
12196         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12197         
12198         /**
12199          * The function used to validate alpha values
12200          * @param {String} value The value
12201          */
12202         'alpha' : function(v){
12203             return alpha.test(v);
12204         },
12205         /**
12206          * The error text to display when the alpha validation function returns false
12207          * @type String
12208          */
12209         'alphaText' : 'This field should only contain letters and _',
12210         /**
12211          * The keystroke filter mask to be applied on alpha input
12212          * @type RegExp
12213          */
12214         'alphaMask' : /[a-z_]/i,
12215
12216         /**
12217          * The function used to validate alphanumeric values
12218          * @param {String} value The value
12219          */
12220         'alphanum' : function(v){
12221             return alphanum.test(v);
12222         },
12223         /**
12224          * The error text to display when the alphanumeric validation function returns false
12225          * @type String
12226          */
12227         'alphanumText' : 'This field should only contain letters, numbers and _',
12228         /**
12229          * The keystroke filter mask to be applied on alphanumeric input
12230          * @type RegExp
12231          */
12232         'alphanumMask' : /[a-z0-9_]/i
12233     };
12234 }();/*
12235  * - LGPL
12236  *
12237  * Input
12238  * 
12239  */
12240
12241 /**
12242  * @class Roo.bootstrap.form.Input
12243  * @extends Roo.bootstrap.Component
12244  * Bootstrap Input class
12245  * @cfg {Boolean} disabled is it disabled
12246  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12247  * @cfg {String} name name of the input
12248  * @cfg {string} fieldLabel - the label associated
12249  * @cfg {string} placeholder - placeholder to put in text.
12250  * @cfg {string} before - input group add on before
12251  * @cfg {string} after - input group add on after
12252  * @cfg {string} size - (lg|sm) or leave empty..
12253  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12254  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12255  * @cfg {Number} md colspan out of 12 for computer-sized screens
12256  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12257  * @cfg {string} value default value of the input
12258  * @cfg {Number} labelWidth set the width of label 
12259  * @cfg {Number} labellg set the width of label (1-12)
12260  * @cfg {Number} labelmd set the width of label (1-12)
12261  * @cfg {Number} labelsm set the width of label (1-12)
12262  * @cfg {Number} labelxs set the width of label (1-12)
12263  * @cfg {String} labelAlign (top|left)
12264  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12265  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12266  * @cfg {String} indicatorpos (left|right) default left
12267  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12268  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12269  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12270  * @cfg {Roo.bootstrap.Button} before Button to show before
12271  * @cfg {Roo.bootstrap.Button} afterButton to show before
12272  * @cfg {String} align (left|center|right) Default left
12273  * @cfg {Boolean} forceFeedback (true|false) Default false
12274  * 
12275  * @constructor
12276  * Create a new Input
12277  * @param {Object} config The config object
12278  */
12279
12280 Roo.bootstrap.form.Input = function(config){
12281     
12282     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12283     
12284     this.addEvents({
12285         /**
12286          * @event focus
12287          * Fires when this field receives input focus.
12288          * @param {Roo.form.Field} this
12289          */
12290         focus : true,
12291         /**
12292          * @event blur
12293          * Fires when this field loses input focus.
12294          * @param {Roo.form.Field} this
12295          */
12296         blur : true,
12297         /**
12298          * @event specialkey
12299          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12300          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12301          * @param {Roo.form.Field} this
12302          * @param {Roo.EventObject} e The event object
12303          */
12304         specialkey : true,
12305         /**
12306          * @event change
12307          * Fires just before the field blurs if the field value has changed.
12308          * @param {Roo.form.Field} this
12309          * @param {Mixed} newValue The new value
12310          * @param {Mixed} oldValue The original value
12311          */
12312         change : true,
12313         /**
12314          * @event invalid
12315          * Fires after the field has been marked as invalid.
12316          * @param {Roo.form.Field} this
12317          * @param {String} msg The validation message
12318          */
12319         invalid : true,
12320         /**
12321          * @event valid
12322          * Fires after the field has been validated with no errors.
12323          * @param {Roo.form.Field} this
12324          */
12325         valid : true,
12326          /**
12327          * @event keyup
12328          * Fires after the key up
12329          * @param {Roo.form.Field} this
12330          * @param {Roo.EventObject}  e The event Object
12331          */
12332         keyup : true,
12333         /**
12334          * @event paste
12335          * Fires after the user pastes into input
12336          * @param {Roo.form.Field} this
12337          * @param {Roo.EventObject}  e The event Object
12338          */
12339         paste : true
12340     });
12341 };
12342
12343 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12344      /**
12345      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12346       automatic validation (defaults to "keyup").
12347      */
12348     validationEvent : "keyup",
12349      /**
12350      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12351      */
12352     validateOnBlur : true,
12353     /**
12354      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12355      */
12356     validationDelay : 250,
12357      /**
12358      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12359      */
12360     focusClass : "x-form-focus",  // not needed???
12361     
12362        
12363     /**
12364      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365      */
12366     invalidClass : "has-warning",
12367     
12368     /**
12369      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12370      */
12371     validClass : "has-success",
12372     
12373     /**
12374      * @cfg {Boolean} hasFeedback (true|false) default true
12375      */
12376     hasFeedback : true,
12377     
12378     /**
12379      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380      */
12381     invalidFeedbackClass : "glyphicon-warning-sign",
12382     
12383     /**
12384      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12385      */
12386     validFeedbackClass : "glyphicon-ok",
12387     
12388     /**
12389      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12390      */
12391     selectOnFocus : false,
12392     
12393      /**
12394      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12395      */
12396     maskRe : null,
12397        /**
12398      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12399      */
12400     vtype : null,
12401     
12402       /**
12403      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12404      */
12405     disableKeyFilter : false,
12406     
12407        /**
12408      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12409      */
12410     disabled : false,
12411      /**
12412      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12413      */
12414     allowBlank : true,
12415     /**
12416      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12417      */
12418     blankText : "Please complete this mandatory field",
12419     
12420      /**
12421      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12422      */
12423     minLength : 0,
12424     /**
12425      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12426      */
12427     maxLength : Number.MAX_VALUE,
12428     /**
12429      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12430      */
12431     minLengthText : "The minimum length for this field is {0}",
12432     /**
12433      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12434      */
12435     maxLengthText : "The maximum length for this field is {0}",
12436   
12437     
12438     /**
12439      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12440      * If available, this function will be called only after the basic validators all return true, and will be passed the
12441      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12442      */
12443     validator : null,
12444     /**
12445      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12446      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12447      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12448      */
12449     regex : null,
12450     /**
12451      * @cfg {String} regexText -- Depricated - use Invalid Text
12452      */
12453     regexText : "",
12454     
12455     /**
12456      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12457      */
12458     invalidText : "",
12459     
12460     
12461     
12462     autocomplete: false,
12463     
12464     
12465     fieldLabel : '',
12466     inputType : 'text',
12467     
12468     name : false,
12469     placeholder: false,
12470     before : false,
12471     after : false,
12472     size : false,
12473     hasFocus : false,
12474     preventMark: false,
12475     isFormField : true,
12476     value : '',
12477     labelWidth : 2,
12478     labelAlign : false,
12479     readOnly : false,
12480     align : false,
12481     formatedValue : false,
12482     forceFeedback : false,
12483     
12484     indicatorpos : 'left',
12485     
12486     labellg : 0,
12487     labelmd : 0,
12488     labelsm : 0,
12489     labelxs : 0,
12490     
12491     capture : '',
12492     accept : '',
12493     
12494     parentLabelAlign : function()
12495     {
12496         var parent = this;
12497         while (parent.parent()) {
12498             parent = parent.parent();
12499             if (typeof(parent.labelAlign) !='undefined') {
12500                 return parent.labelAlign;
12501             }
12502         }
12503         return 'left';
12504         
12505     },
12506     
12507     getAutoCreate : function()
12508     {
12509         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12510         
12511         var id = Roo.id();
12512         
12513         var cfg = {};
12514         
12515         if(this.inputType != 'hidden'){
12516             cfg.cls = 'form-group' //input-group
12517         }
12518         
12519         var input =  {
12520             tag: 'input',
12521             id : id,
12522             type : this.inputType,
12523             value : this.value,
12524             cls : 'form-control',
12525             placeholder : this.placeholder || '',
12526             autocomplete : this.autocomplete || 'new-password'
12527         };
12528         if (this.inputType == 'file') {
12529             input.style = 'overflow:hidden'; // why not in CSS?
12530         }
12531         
12532         if(this.capture.length){
12533             input.capture = this.capture;
12534         }
12535         
12536         if(this.accept.length){
12537             input.accept = this.accept + "/*";
12538         }
12539         
12540         if(this.align){
12541             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12542         }
12543         
12544         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12545             input.maxLength = this.maxLength;
12546         }
12547         
12548         if (this.disabled) {
12549             input.disabled=true;
12550         }
12551         
12552         if (this.readOnly) {
12553             input.readonly=true;
12554         }
12555         
12556         if (this.name) {
12557             input.name = this.name;
12558         }
12559         
12560         if (this.size) {
12561             input.cls += ' input-' + this.size;
12562         }
12563         
12564         var settings=this;
12565         ['xs','sm','md','lg'].map(function(size){
12566             if (settings[size]) {
12567                 cfg.cls += ' col-' + size + '-' + settings[size];
12568             }
12569         });
12570         
12571         var inputblock = input;
12572         
12573         var feedback = {
12574             tag: 'span',
12575             cls: 'glyphicon form-control-feedback'
12576         };
12577             
12578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12579             
12580             inputblock = {
12581                 cls : 'has-feedback',
12582                 cn :  [
12583                     input,
12584                     feedback
12585                 ] 
12586             };  
12587         }
12588         
12589         if (this.before || this.after) {
12590             
12591             inputblock = {
12592                 cls : 'input-group',
12593                 cn :  [] 
12594             };
12595             
12596             if (this.before && typeof(this.before) == 'string') {
12597                 
12598                 inputblock.cn.push({
12599                     tag :'span',
12600                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12601                     html : this.before
12602                 });
12603             }
12604             if (this.before && typeof(this.before) == 'object') {
12605                 this.before = Roo.factory(this.before);
12606                 
12607                 inputblock.cn.push({
12608                     tag :'span',
12609                     cls : 'roo-input-before input-group-prepend   input-group-' +
12610                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12611                 });
12612             }
12613             
12614             inputblock.cn.push(input);
12615             
12616             if (this.after && typeof(this.after) == 'string') {
12617                 inputblock.cn.push({
12618                     tag :'span',
12619                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12620                     html : this.after
12621                 });
12622             }
12623             if (this.after && typeof(this.after) == 'object') {
12624                 this.after = Roo.factory(this.after);
12625                 
12626                 inputblock.cn.push({
12627                     tag :'span',
12628                     cls : 'roo-input-after input-group-append  input-group-' +
12629                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12630                 });
12631             }
12632             
12633             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12634                 inputblock.cls += ' has-feedback';
12635                 inputblock.cn.push(feedback);
12636             }
12637         };
12638         var indicator = {
12639             tag : 'i',
12640             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12641             tooltip : 'This field is required'
12642         };
12643         if (this.allowBlank ) {
12644             indicator.style = this.allowBlank ? ' display:none' : '';
12645         }
12646         if (align ==='left' && this.fieldLabel.length) {
12647             
12648             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12649             
12650             cfg.cn = [
12651                 indicator,
12652                 {
12653                     tag: 'label',
12654                     'for' :  id,
12655                     cls : 'control-label col-form-label',
12656                     html : this.fieldLabel
12657
12658                 },
12659                 {
12660                     cls : "", 
12661                     cn: [
12662                         inputblock
12663                     ]
12664                 }
12665             ];
12666             
12667             var labelCfg = cfg.cn[1];
12668             var contentCfg = cfg.cn[2];
12669             
12670             if(this.indicatorpos == 'right'){
12671                 cfg.cn = [
12672                     {
12673                         tag: 'label',
12674                         'for' :  id,
12675                         cls : 'control-label col-form-label',
12676                         cn : [
12677                             {
12678                                 tag : 'span',
12679                                 html : this.fieldLabel
12680                             },
12681                             indicator
12682                         ]
12683                     },
12684                     {
12685                         cls : "",
12686                         cn: [
12687                             inputblock
12688                         ]
12689                     }
12690
12691                 ];
12692                 
12693                 labelCfg = cfg.cn[0];
12694                 contentCfg = cfg.cn[1];
12695             
12696             }
12697             
12698             if(this.labelWidth > 12){
12699                 labelCfg.style = "width: " + this.labelWidth + 'px';
12700             }
12701             
12702             if(this.labelWidth < 13 && this.labelmd == 0){
12703                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12704             }
12705             
12706             if(this.labellg > 0){
12707                 labelCfg.cls += ' col-lg-' + this.labellg;
12708                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12709             }
12710             
12711             if(this.labelmd > 0){
12712                 labelCfg.cls += ' col-md-' + this.labelmd;
12713                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12714             }
12715             
12716             if(this.labelsm > 0){
12717                 labelCfg.cls += ' col-sm-' + this.labelsm;
12718                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12719             }
12720             
12721             if(this.labelxs > 0){
12722                 labelCfg.cls += ' col-xs-' + this.labelxs;
12723                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12724             }
12725             
12726             
12727         } else if ( this.fieldLabel.length) {
12728                 
12729             
12730             
12731             cfg.cn = [
12732                 {
12733                     tag : 'i',
12734                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12735                     tooltip : 'This field is required',
12736                     style : this.allowBlank ? ' display:none' : '' 
12737                 },
12738                 {
12739                     tag: 'label',
12740                    //cls : 'input-group-addon',
12741                     html : this.fieldLabel
12742
12743                 },
12744
12745                inputblock
12746
12747            ];
12748            
12749            if(this.indicatorpos == 'right'){
12750        
12751                 cfg.cn = [
12752                     {
12753                         tag: 'label',
12754                        //cls : 'input-group-addon',
12755                         html : this.fieldLabel
12756
12757                     },
12758                     {
12759                         tag : 'i',
12760                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12761                         tooltip : 'This field is required',
12762                         style : this.allowBlank ? ' display:none' : '' 
12763                     },
12764
12765                    inputblock
12766
12767                ];
12768
12769             }
12770
12771         } else {
12772             
12773             cfg.cn = [
12774
12775                     inputblock
12776
12777             ];
12778                 
12779                 
12780         };
12781         
12782         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12783            cfg.cls += ' navbar-form';
12784         }
12785         
12786         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12787             // on BS4 we do this only if not form 
12788             cfg.cls += ' navbar-form';
12789             cfg.tag = 'li';
12790         }
12791         
12792         return cfg;
12793         
12794     },
12795     /**
12796      * return the real input element.
12797      */
12798     inputEl: function ()
12799     {
12800         return this.el.select('input.form-control',true).first();
12801     },
12802     
12803     tooltipEl : function()
12804     {
12805         return this.inputEl();
12806     },
12807     
12808     indicatorEl : function()
12809     {
12810         if (Roo.bootstrap.version == 4) {
12811             return false; // not enabled in v4 yet.
12812         }
12813         
12814         var indicator = this.el.select('i.roo-required-indicator',true).first();
12815         
12816         if(!indicator){
12817             return false;
12818         }
12819         
12820         return indicator;
12821         
12822     },
12823     
12824     setDisabled : function(v)
12825     {
12826         var i  = this.inputEl().dom;
12827         if (!v) {
12828             i.removeAttribute('disabled');
12829             return;
12830             
12831         }
12832         i.setAttribute('disabled','true');
12833     },
12834     initEvents : function()
12835     {
12836           
12837         this.inputEl().on("keydown" , this.fireKey,  this);
12838         this.inputEl().on("focus", this.onFocus,  this);
12839         this.inputEl().on("blur", this.onBlur,  this);
12840         
12841         this.inputEl().relayEvent('keyup', this);
12842         this.inputEl().relayEvent('paste', this);
12843         
12844         this.indicator = this.indicatorEl();
12845         
12846         if(this.indicator){
12847             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12848         }
12849  
12850         // reference to original value for reset
12851         this.originalValue = this.getValue();
12852         //Roo.form.TextField.superclass.initEvents.call(this);
12853         if(this.validationEvent == 'keyup'){
12854             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12855             this.inputEl().on('keyup', this.filterValidation, this);
12856         }
12857         else if(this.validationEvent !== false){
12858             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12859         }
12860         
12861         if(this.selectOnFocus){
12862             this.on("focus", this.preFocus, this);
12863             
12864         }
12865         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12866             this.inputEl().on("keypress", this.filterKeys, this);
12867         } else {
12868             this.inputEl().relayEvent('keypress', this);
12869         }
12870        /* if(this.grow){
12871             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12872             this.el.on("click", this.autoSize,  this);
12873         }
12874         */
12875         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12876             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12877         }
12878         
12879         if (typeof(this.before) == 'object') {
12880             this.before.render(this.el.select('.roo-input-before',true).first());
12881         }
12882         if (typeof(this.after) == 'object') {
12883             this.after.render(this.el.select('.roo-input-after',true).first());
12884         }
12885         
12886         this.inputEl().on('change', this.onChange, this);
12887         
12888     },
12889     filterValidation : function(e){
12890         if(!e.isNavKeyPress()){
12891             this.validationTask.delay(this.validationDelay);
12892         }
12893     },
12894      /**
12895      * Validates the field value
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validate : function(){
12899         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12900         if(this.disabled || this.validateValue(this.getRawValue())){
12901             this.markValid();
12902             return true;
12903         }
12904         
12905         this.markInvalid();
12906         return false;
12907     },
12908     
12909     
12910     /**
12911      * Validates a value according to the field's validation rules and marks the field as invalid
12912      * if the validation fails
12913      * @param {Mixed} value The value to validate
12914      * @return {Boolean} True if the value is valid, else false
12915      */
12916     validateValue : function(value)
12917     {
12918         if(this.getVisibilityEl().hasClass('hidden')){
12919             return true;
12920         }
12921         
12922         if(value.length < 1)  { // if it's blank
12923             if(this.allowBlank){
12924                 return true;
12925             }
12926             return false;
12927         }
12928         
12929         if(value.length < this.minLength){
12930             return false;
12931         }
12932         if(value.length > this.maxLength){
12933             return false;
12934         }
12935         if(this.vtype){
12936             var vt = Roo.form.VTypes;
12937             if(!vt[this.vtype](value, this)){
12938                 return false;
12939             }
12940         }
12941         if(typeof this.validator == "function"){
12942             var msg = this.validator(value);
12943             if(msg !== true){
12944                 return false;
12945             }
12946             if (typeof(msg) == 'string') {
12947                 this.invalidText = msg;
12948             }
12949         }
12950         
12951         if(this.regex && !this.regex.test(value)){
12952             return false;
12953         }
12954         
12955         return true;
12956     },
12957     
12958      // private
12959     fireKey : function(e){
12960         //Roo.log('field ' + e.getKey());
12961         if(e.isNavKeyPress()){
12962             this.fireEvent("specialkey", this, e);
12963         }
12964     },
12965     focus : function (selectText){
12966         if(this.rendered){
12967             this.inputEl().focus();
12968             if(selectText === true){
12969                 this.inputEl().dom.select();
12970             }
12971         }
12972         return this;
12973     } ,
12974     
12975     onFocus : function(){
12976         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12977            // this.el.addClass(this.focusClass);
12978         }
12979         if(!this.hasFocus){
12980             this.hasFocus = true;
12981             this.startValue = this.getValue();
12982             this.fireEvent("focus", this);
12983         }
12984     },
12985     
12986     beforeBlur : Roo.emptyFn,
12987
12988     
12989     // private
12990     onBlur : function(){
12991         this.beforeBlur();
12992         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12993             //this.el.removeClass(this.focusClass);
12994         }
12995         this.hasFocus = false;
12996         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12997             this.validate();
12998         }
12999         var v = this.getValue();
13000         if(String(v) !== String(this.startValue)){
13001             this.fireEvent('change', this, v, this.startValue);
13002         }
13003         this.fireEvent("blur", this);
13004     },
13005     
13006     onChange : function(e)
13007     {
13008         var v = this.getValue();
13009         if(String(v) !== String(this.startValue)){
13010             this.fireEvent('change', this, v, this.startValue);
13011         }
13012         
13013     },
13014     
13015     /**
13016      * Resets the current field value to the originally loaded value and clears any validation messages
13017      */
13018     reset : function(){
13019         this.setValue(this.originalValue);
13020         this.validate();
13021     },
13022      /**
13023      * Returns the name of the field
13024      * @return {Mixed} name The name field
13025      */
13026     getName: function(){
13027         return this.name;
13028     },
13029      /**
13030      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13031      * @return {Mixed} value The field value
13032      */
13033     getValue : function(){
13034         
13035         var v = this.inputEl().getValue();
13036         
13037         return v;
13038     },
13039     /**
13040      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13041      * @return {Mixed} value The field value
13042      */
13043     getRawValue : function(){
13044         var v = this.inputEl().getValue();
13045         
13046         return v;
13047     },
13048     
13049     /**
13050      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13051      * @param {Mixed} value The value to set
13052      */
13053     setRawValue : function(v){
13054         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13055     },
13056     
13057     selectText : function(start, end){
13058         var v = this.getRawValue();
13059         if(v.length > 0){
13060             start = start === undefined ? 0 : start;
13061             end = end === undefined ? v.length : end;
13062             var d = this.inputEl().dom;
13063             if(d.setSelectionRange){
13064                 d.setSelectionRange(start, end);
13065             }else if(d.createTextRange){
13066                 var range = d.createTextRange();
13067                 range.moveStart("character", start);
13068                 range.moveEnd("character", v.length-end);
13069                 range.select();
13070             }
13071         }
13072     },
13073     
13074     /**
13075      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13076      * @param {Mixed} value The value to set
13077      */
13078     setValue : function(v){
13079         this.value = v;
13080         if(this.rendered){
13081             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13082             this.validate();
13083         }
13084     },
13085     
13086     /*
13087     processValue : function(value){
13088         if(this.stripCharsRe){
13089             var newValue = value.replace(this.stripCharsRe, '');
13090             if(newValue !== value){
13091                 this.setRawValue(newValue);
13092                 return newValue;
13093             }
13094         }
13095         return value;
13096     },
13097   */
13098     preFocus : function(){
13099         
13100         if(this.selectOnFocus){
13101             this.inputEl().dom.select();
13102         }
13103     },
13104     filterKeys : function(e){
13105         var k = e.getKey();
13106         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13107             return;
13108         }
13109         var c = e.getCharCode(), cc = String.fromCharCode(c);
13110         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13111             return;
13112         }
13113         if(!this.maskRe.test(cc)){
13114             e.stopEvent();
13115         }
13116     },
13117      /**
13118      * Clear any invalid styles/messages for this field
13119      */
13120     clearInvalid : function(){
13121         
13122         if(!this.el || this.preventMark){ // not rendered
13123             return;
13124         }
13125         
13126         
13127         this.el.removeClass([this.invalidClass, 'is-invalid']);
13128         
13129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13130             
13131             var feedback = this.el.select('.form-control-feedback', true).first();
13132             
13133             if(feedback){
13134                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13135             }
13136             
13137         }
13138         
13139         if(this.indicator){
13140             this.indicator.removeClass('visible');
13141             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13142         }
13143         
13144         this.fireEvent('valid', this);
13145     },
13146     
13147      /**
13148      * Mark this field as valid
13149      */
13150     markValid : function()
13151     {
13152         if(!this.el  || this.preventMark){ // not rendered...
13153             return;
13154         }
13155         
13156         this.el.removeClass([this.invalidClass, this.validClass]);
13157         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13158
13159         var feedback = this.el.select('.form-control-feedback', true).first();
13160             
13161         if(feedback){
13162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13163         }
13164         
13165         if(this.indicator){
13166             this.indicator.removeClass('visible');
13167             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13168         }
13169         
13170         if(this.disabled){
13171             return;
13172         }
13173         
13174            
13175         if(this.allowBlank && !this.getRawValue().length){
13176             return;
13177         }
13178         if (Roo.bootstrap.version == 3) {
13179             this.el.addClass(this.validClass);
13180         } else {
13181             this.inputEl().addClass('is-valid');
13182         }
13183
13184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13185             
13186             var feedback = this.el.select('.form-control-feedback', true).first();
13187             
13188             if(feedback){
13189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13191             }
13192             
13193         }
13194         
13195         this.fireEvent('valid', this);
13196     },
13197     
13198      /**
13199      * Mark this field as invalid
13200      * @param {String} msg The validation message
13201      */
13202     markInvalid : function(msg)
13203     {
13204         if(!this.el  || this.preventMark){ // not rendered
13205             return;
13206         }
13207         
13208         this.el.removeClass([this.invalidClass, this.validClass]);
13209         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13210         
13211         var feedback = this.el.select('.form-control-feedback', true).first();
13212             
13213         if(feedback){
13214             this.el.select('.form-control-feedback', true).first().removeClass(
13215                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13216         }
13217
13218         if(this.disabled){
13219             return;
13220         }
13221         
13222         if(this.allowBlank && !this.getRawValue().length){
13223             return;
13224         }
13225         
13226         if(this.indicator){
13227             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13228             this.indicator.addClass('visible');
13229         }
13230         if (Roo.bootstrap.version == 3) {
13231             this.el.addClass(this.invalidClass);
13232         } else {
13233             this.inputEl().addClass('is-invalid');
13234         }
13235         
13236         
13237         
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 
13245                 if(this.getValue().length || this.forceFeedback){
13246                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247                 }
13248                 
13249             }
13250             
13251         }
13252         
13253         this.fireEvent('invalid', this, msg);
13254     },
13255     // private
13256     SafariOnKeyDown : function(event)
13257     {
13258         // this is a workaround for a password hang bug on chrome/ webkit.
13259         if (this.inputEl().dom.type != 'password') {
13260             return;
13261         }
13262         
13263         var isSelectAll = false;
13264         
13265         if(this.inputEl().dom.selectionEnd > 0){
13266             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13267         }
13268         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13269             event.preventDefault();
13270             this.setValue('');
13271             return;
13272         }
13273         
13274         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13275             
13276             event.preventDefault();
13277             // this is very hacky as keydown always get's upper case.
13278             //
13279             var cc = String.fromCharCode(event.getCharCode());
13280             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13281             
13282         }
13283     },
13284     adjustWidth : function(tag, w){
13285         tag = tag.toLowerCase();
13286         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13287             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13288                 if(tag == 'input'){
13289                     return w + 2;
13290                 }
13291                 if(tag == 'textarea'){
13292                     return w-2;
13293                 }
13294             }else if(Roo.isOpera){
13295                 if(tag == 'input'){
13296                     return w + 2;
13297                 }
13298                 if(tag == 'textarea'){
13299                     return w-2;
13300                 }
13301             }
13302         }
13303         return w;
13304     },
13305     
13306     setFieldLabel : function(v)
13307     {
13308         if(!this.rendered){
13309             return;
13310         }
13311         
13312         if(this.indicatorEl()){
13313             var ar = this.el.select('label > span',true);
13314             
13315             if (ar.elements.length) {
13316                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317                 this.fieldLabel = v;
13318                 return;
13319             }
13320             
13321             var br = this.el.select('label',true);
13322             
13323             if(br.elements.length) {
13324                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13325                 this.fieldLabel = v;
13326                 return;
13327             }
13328             
13329             Roo.log('Cannot Found any of label > span || label in input');
13330             return;
13331         }
13332         
13333         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13334         this.fieldLabel = v;
13335         
13336         
13337     }
13338 });
13339
13340  
13341 /*
13342  * - LGPL
13343  *
13344  * Input
13345  * 
13346  */
13347
13348 /**
13349  * @class Roo.bootstrap.form.TextArea
13350  * @extends Roo.bootstrap.form.Input
13351  * Bootstrap TextArea class
13352  * @cfg {Number} cols Specifies the visible width of a text area
13353  * @cfg {Number} rows Specifies the visible number of lines in a text area
13354  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13355  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13356  * @cfg {string} html text
13357  * 
13358  * @constructor
13359  * Create a new TextArea
13360  * @param {Object} config The config object
13361  */
13362
13363 Roo.bootstrap.form.TextArea = function(config){
13364     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13365    
13366 };
13367
13368 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13369      
13370     cols : false,
13371     rows : 5,
13372     readOnly : false,
13373     warp : 'soft',
13374     resize : false,
13375     value: false,
13376     html: false,
13377     
13378     getAutoCreate : function(){
13379         
13380         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13381         
13382         var id = Roo.id();
13383         
13384         var cfg = {};
13385         
13386         if(this.inputType != 'hidden'){
13387             cfg.cls = 'form-group' //input-group
13388         }
13389         
13390         var input =  {
13391             tag: 'textarea',
13392             id : id,
13393             warp : this.warp,
13394             rows : this.rows,
13395             value : this.value || '',
13396             html: this.html || '',
13397             cls : 'form-control',
13398             placeholder : this.placeholder || '' 
13399             
13400         };
13401         
13402         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13403             input.maxLength = this.maxLength;
13404         }
13405         
13406         if(this.resize){
13407             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13408         }
13409         
13410         if(this.cols){
13411             input.cols = this.cols;
13412         }
13413         
13414         if (this.readOnly) {
13415             input.readonly = true;
13416         }
13417         
13418         if (this.name) {
13419             input.name = this.name;
13420         }
13421         
13422         if (this.size) {
13423             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13424         }
13425         
13426         var settings=this;
13427         ['xs','sm','md','lg'].map(function(size){
13428             if (settings[size]) {
13429                 cfg.cls += ' col-' + size + '-' + settings[size];
13430             }
13431         });
13432         
13433         var inputblock = input;
13434         
13435         if(this.hasFeedback && !this.allowBlank){
13436             
13437             var feedback = {
13438                 tag: 'span',
13439                 cls: 'glyphicon form-control-feedback'
13440             };
13441
13442             inputblock = {
13443                 cls : 'has-feedback',
13444                 cn :  [
13445                     input,
13446                     feedback
13447                 ] 
13448             };  
13449         }
13450         
13451         
13452         if (this.before || this.after) {
13453             
13454             inputblock = {
13455                 cls : 'input-group',
13456                 cn :  [] 
13457             };
13458             if (this.before) {
13459                 inputblock.cn.push({
13460                     tag :'span',
13461                     cls : 'input-group-addon',
13462                     html : this.before
13463                 });
13464             }
13465             
13466             inputblock.cn.push(input);
13467             
13468             if(this.hasFeedback && !this.allowBlank){
13469                 inputblock.cls += ' has-feedback';
13470                 inputblock.cn.push(feedback);
13471             }
13472             
13473             if (this.after) {
13474                 inputblock.cn.push({
13475                     tag :'span',
13476                     cls : 'input-group-addon',
13477                     html : this.after
13478                 });
13479             }
13480             
13481         }
13482         
13483         if (align ==='left' && this.fieldLabel.length) {
13484             cfg.cn = [
13485                 {
13486                     tag: 'label',
13487                     'for' :  id,
13488                     cls : 'control-label',
13489                     html : this.fieldLabel
13490                 },
13491                 {
13492                     cls : "",
13493                     cn: [
13494                         inputblock
13495                     ]
13496                 }
13497
13498             ];
13499             
13500             if(this.labelWidth > 12){
13501                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13502             }
13503
13504             if(this.labelWidth < 13 && this.labelmd == 0){
13505                 this.labelmd = this.labelWidth;
13506             }
13507
13508             if(this.labellg > 0){
13509                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13510                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13511             }
13512
13513             if(this.labelmd > 0){
13514                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13515                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13516             }
13517
13518             if(this.labelsm > 0){
13519                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13520                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13521             }
13522
13523             if(this.labelxs > 0){
13524                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13525                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13526             }
13527             
13528         } else if ( this.fieldLabel.length) {
13529             cfg.cn = [
13530
13531                {
13532                    tag: 'label',
13533                    //cls : 'input-group-addon',
13534                    html : this.fieldLabel
13535
13536                },
13537
13538                inputblock
13539
13540            ];
13541
13542         } else {
13543
13544             cfg.cn = [
13545
13546                 inputblock
13547
13548             ];
13549                 
13550         }
13551         
13552         if (this.disabled) {
13553             input.disabled=true;
13554         }
13555         
13556         return cfg;
13557         
13558     },
13559     /**
13560      * return the real textarea element.
13561      */
13562     inputEl: function ()
13563     {
13564         return this.el.select('textarea.form-control',true).first();
13565     },
13566     
13567     /**
13568      * Clear any invalid styles/messages for this field
13569      */
13570     clearInvalid : function()
13571     {
13572         
13573         if(!this.el || this.preventMark){ // not rendered
13574             return;
13575         }
13576         
13577         var label = this.el.select('label', true).first();
13578         var icon = this.el.select('i.fa-star', true).first();
13579         
13580         if(label && icon){
13581             icon.remove();
13582         }
13583         this.el.removeClass( this.validClass);
13584         this.inputEl().removeClass('is-invalid');
13585          
13586         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13587             
13588             var feedback = this.el.select('.form-control-feedback', true).first();
13589             
13590             if(feedback){
13591                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13592             }
13593             
13594         }
13595         
13596         this.fireEvent('valid', this);
13597     },
13598     
13599      /**
13600      * Mark this field as valid
13601      */
13602     markValid : function()
13603     {
13604         if(!this.el  || this.preventMark){ // not rendered
13605             return;
13606         }
13607         
13608         this.el.removeClass([this.invalidClass, this.validClass]);
13609         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13610         
13611         var feedback = this.el.select('.form-control-feedback', true).first();
13612             
13613         if(feedback){
13614             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13615         }
13616
13617         if(this.disabled || this.allowBlank){
13618             return;
13619         }
13620         
13621         var label = this.el.select('label', true).first();
13622         var icon = this.el.select('i.fa-star', true).first();
13623         
13624         if(label && icon){
13625             icon.remove();
13626         }
13627         if (Roo.bootstrap.version == 3) {
13628             this.el.addClass(this.validClass);
13629         } else {
13630             this.inputEl().addClass('is-valid');
13631         }
13632         
13633         
13634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13635             
13636             var feedback = this.el.select('.form-control-feedback', true).first();
13637             
13638             if(feedback){
13639                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13640                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13641             }
13642             
13643         }
13644         
13645         this.fireEvent('valid', this);
13646     },
13647     
13648      /**
13649      * Mark this field as invalid
13650      * @param {String} msg The validation message
13651      */
13652     markInvalid : function(msg)
13653     {
13654         if(!this.el  || this.preventMark){ // not rendered
13655             return;
13656         }
13657         
13658         this.el.removeClass([this.invalidClass, this.validClass]);
13659         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13660         
13661         var feedback = this.el.select('.form-control-feedback', true).first();
13662             
13663         if(feedback){
13664             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13665         }
13666
13667         if(this.disabled || this.allowBlank){
13668             return;
13669         }
13670         
13671         var label = this.el.select('label', true).first();
13672         var icon = this.el.select('i.fa-star', true).first();
13673         
13674         if(!this.getValue().length && label && !icon){
13675             this.el.createChild({
13676                 tag : 'i',
13677                 cls : 'text-danger fa fa-lg fa-star',
13678                 tooltip : 'This field is required',
13679                 style : 'margin-right:5px;'
13680             }, label, true);
13681         }
13682         
13683         if (Roo.bootstrap.version == 3) {
13684             this.el.addClass(this.invalidClass);
13685         } else {
13686             this.inputEl().addClass('is-invalid');
13687         }
13688         
13689         // fixme ... this may be depricated need to test..
13690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13691             
13692             var feedback = this.el.select('.form-control-feedback', true).first();
13693             
13694             if(feedback){
13695                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13696                 
13697                 if(this.getValue().length || this.forceFeedback){
13698                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13699                 }
13700                 
13701             }
13702             
13703         }
13704         
13705         this.fireEvent('invalid', this, msg);
13706     }
13707 });
13708
13709  
13710 /*
13711  * - LGPL
13712  *
13713  * trigger field - base class for combo..
13714  * 
13715  */
13716  
13717 /**
13718  * @class Roo.bootstrap.form.TriggerField
13719  * @extends Roo.bootstrap.form.Input
13720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13723  * for which you can provide a custom implementation.  For example:
13724  * <pre><code>
13725 var trigger = new Roo.bootstrap.form.TriggerField();
13726 trigger.onTriggerClick = myTriggerFn;
13727 trigger.applyTo('my-field');
13728 </code></pre>
13729  *
13730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13731  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13734  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13735
13736  * @constructor
13737  * Create a new TriggerField.
13738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13739  * to the base TextField)
13740  */
13741 Roo.bootstrap.form.TriggerField = function(config){
13742     this.mimicing = false;
13743     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13744 };
13745
13746 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13747     /**
13748      * @cfg {String} triggerClass A CSS class to apply to the trigger
13749      */
13750      /**
13751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13752      */
13753     hideTrigger:false,
13754
13755     /**
13756      * @cfg {Boolean} removable (true|false) special filter default false
13757      */
13758     removable : false,
13759     
13760     /** @cfg {Boolean} grow @hide */
13761     /** @cfg {Number} growMin @hide */
13762     /** @cfg {Number} growMax @hide */
13763
13764     /**
13765      * @hide 
13766      * @method
13767      */
13768     autoSize: Roo.emptyFn,
13769     // private
13770     monitorTab : true,
13771     // private
13772     deferHeight : true,
13773
13774     
13775     actionMode : 'wrap',
13776     
13777     caret : false,
13778     
13779     
13780     getAutoCreate : function(){
13781        
13782         var align = this.labelAlign || this.parentLabelAlign();
13783         
13784         var id = Roo.id();
13785         
13786         var cfg = {
13787             cls: 'form-group' //input-group
13788         };
13789         
13790         
13791         var input =  {
13792             tag: 'input',
13793             id : id,
13794             type : this.inputType,
13795             cls : 'form-control',
13796             autocomplete: 'new-password',
13797             placeholder : this.placeholder || '' 
13798             
13799         };
13800         if (this.name) {
13801             input.name = this.name;
13802         }
13803         if (this.size) {
13804             input.cls += ' input-' + this.size;
13805         }
13806         
13807         if (this.disabled) {
13808             input.disabled=true;
13809         }
13810         
13811         var inputblock = input;
13812         
13813         if(this.hasFeedback && !this.allowBlank){
13814             
13815             var feedback = {
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             };
13819             
13820             if(this.removable && !this.editable  ){
13821                 inputblock = {
13822                     cls : 'has-feedback',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         },
13830                         feedback
13831                     ] 
13832                 };
13833             } else {
13834                 inputblock = {
13835                     cls : 'has-feedback',
13836                     cn :  [
13837                         inputblock,
13838                         feedback
13839                     ] 
13840                 };
13841             }
13842
13843         } else {
13844             if(this.removable && !this.editable ){
13845                 inputblock = {
13846                     cls : 'roo-removable',
13847                     cn :  [
13848                         inputblock,
13849                         {
13850                             tag: 'button',
13851                             html : 'x',
13852                             cls : 'roo-combo-removable-btn close'
13853                         }
13854                     ] 
13855                 };
13856             }
13857         }
13858         
13859         if (this.before || this.after) {
13860             
13861             inputblock = {
13862                 cls : 'input-group',
13863                 cn :  [] 
13864             };
13865             if (this.before) {
13866                 inputblock.cn.push({
13867                     tag :'span',
13868                     cls : 'input-group-addon input-group-prepend input-group-text',
13869                     html : this.before
13870                 });
13871             }
13872             
13873             inputblock.cn.push(input);
13874             
13875             if(this.hasFeedback && !this.allowBlank){
13876                 inputblock.cls += ' has-feedback';
13877                 inputblock.cn.push(feedback);
13878             }
13879             
13880             if (this.after) {
13881                 inputblock.cn.push({
13882                     tag :'span',
13883                     cls : 'input-group-addon input-group-append input-group-text',
13884                     html : this.after
13885                 });
13886             }
13887             
13888         };
13889         
13890       
13891         
13892         var ibwrap = inputblock;
13893         
13894         if(this.multiple){
13895             ibwrap = {
13896                 tag: 'ul',
13897                 cls: 'roo-select2-choices',
13898                 cn:[
13899                     {
13900                         tag: 'li',
13901                         cls: 'roo-select2-search-field',
13902                         cn: [
13903
13904                             inputblock
13905                         ]
13906                     }
13907                 ]
13908             };
13909                 
13910         }
13911         
13912         var combobox = {
13913             cls: 'roo-select2-container input-group',
13914             cn: [
13915                  {
13916                     tag: 'input',
13917                     type : 'hidden',
13918                     cls: 'form-hidden-field'
13919                 },
13920                 ibwrap
13921             ]
13922         };
13923         
13924         if(!this.multiple && this.showToggleBtn){
13925             
13926             var caret = {
13927                         tag: 'span',
13928                         cls: 'caret'
13929              };
13930             if (this.caret != false) {
13931                 caret = {
13932                      tag: 'i',
13933                      cls: 'fa fa-' + this.caret
13934                 };
13935                 
13936             }
13937             
13938             combobox.cn.push({
13939                 tag :'span',
13940                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13941                 cn : [
13942                     Roo.bootstrap.version == 3 ? caret : '',
13943                     {
13944                         tag: 'span',
13945                         cls: 'combobox-clear',
13946                         cn  : [
13947                             {
13948                                 tag : 'i',
13949                                 cls: 'icon-remove'
13950                             }
13951                         ]
13952                     }
13953                 ]
13954
13955             })
13956         }
13957         
13958         if(this.multiple){
13959             combobox.cls += ' roo-select2-container-multi';
13960         }
13961          var indicator = {
13962             tag : 'i',
13963             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13964             tooltip : 'This field is required'
13965         };
13966         if (Roo.bootstrap.version == 4) {
13967             indicator = {
13968                 tag : 'i',
13969                 style : 'display:none'
13970             };
13971         }
13972         
13973         
13974         if (align ==='left' && this.fieldLabel.length) {
13975             
13976             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13977
13978             cfg.cn = [
13979                 indicator,
13980                 {
13981                     tag: 'label',
13982                     'for' :  id,
13983                     cls : 'control-label',
13984                     html : this.fieldLabel
13985
13986                 },
13987                 {
13988                     cls : "", 
13989                     cn: [
13990                         combobox
13991                     ]
13992                 }
13993
13994             ];
13995             
13996             var labelCfg = cfg.cn[1];
13997             var contentCfg = cfg.cn[2];
13998             
13999             if(this.indicatorpos == 'right'){
14000                 cfg.cn = [
14001                     {
14002                         tag: 'label',
14003                         'for' :  id,
14004                         cls : 'control-label',
14005                         cn : [
14006                             {
14007                                 tag : 'span',
14008                                 html : this.fieldLabel
14009                             },
14010                             indicator
14011                         ]
14012                     },
14013                     {
14014                         cls : "", 
14015                         cn: [
14016                             combobox
14017                         ]
14018                     }
14019
14020                 ];
14021                 
14022                 labelCfg = cfg.cn[0];
14023                 contentCfg = cfg.cn[1];
14024             }
14025             
14026             if(this.labelWidth > 12){
14027                 labelCfg.style = "width: " + this.labelWidth + 'px';
14028             }
14029             
14030             if(this.labelWidth < 13 && this.labelmd == 0){
14031                 this.labelmd = this.labelWidth;
14032             }
14033             
14034             if(this.labellg > 0){
14035                 labelCfg.cls += ' col-lg-' + this.labellg;
14036                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14037             }
14038             
14039             if(this.labelmd > 0){
14040                 labelCfg.cls += ' col-md-' + this.labelmd;
14041                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14042             }
14043             
14044             if(this.labelsm > 0){
14045                 labelCfg.cls += ' col-sm-' + this.labelsm;
14046                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14047             }
14048             
14049             if(this.labelxs > 0){
14050                 labelCfg.cls += ' col-xs-' + this.labelxs;
14051                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14052             }
14053             
14054         } else if ( this.fieldLabel.length) {
14055 //                Roo.log(" label");
14056             cfg.cn = [
14057                 indicator,
14058                {
14059                    tag: 'label',
14060                    //cls : 'input-group-addon',
14061                    html : this.fieldLabel
14062
14063                },
14064
14065                combobox
14066
14067             ];
14068             
14069             if(this.indicatorpos == 'right'){
14070                 
14071                 cfg.cn = [
14072                     {
14073                        tag: 'label',
14074                        cn : [
14075                            {
14076                                tag : 'span',
14077                                html : this.fieldLabel
14078                            },
14079                            indicator
14080                        ]
14081
14082                     },
14083                     combobox
14084
14085                 ];
14086
14087             }
14088
14089         } else {
14090             
14091 //                Roo.log(" no label && no align");
14092                 cfg = combobox
14093                      
14094                 
14095         }
14096         
14097         var settings=this;
14098         ['xs','sm','md','lg'].map(function(size){
14099             if (settings[size]) {
14100                 cfg.cls += ' col-' + size + '-' + settings[size];
14101             }
14102         });
14103         
14104         return cfg;
14105         
14106     },
14107     
14108     
14109     
14110     // private
14111     onResize : function(w, h){
14112 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14113 //        if(typeof w == 'number'){
14114 //            var x = w - this.trigger.getWidth();
14115 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14116 //            this.trigger.setStyle('left', x+'px');
14117 //        }
14118     },
14119
14120     // private
14121     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14122
14123     // private
14124     getResizeEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     getPositionEl : function(){
14130         return this.inputEl();
14131     },
14132
14133     // private
14134     alignErrorIcon : function(){
14135         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14136     },
14137
14138     // private
14139     initEvents : function(){
14140         
14141         this.createList();
14142         
14143         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14144         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14145         if(!this.multiple && this.showToggleBtn){
14146             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14147             if(this.hideTrigger){
14148                 this.trigger.setDisplayed(false);
14149             }
14150             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14151         }
14152         
14153         if(this.multiple){
14154             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14155         }
14156         
14157         if(this.removable && !this.editable && !this.tickable){
14158             var close = this.closeTriggerEl();
14159             
14160             if(close){
14161                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14162                 close.on('click', this.removeBtnClick, this, close);
14163             }
14164         }
14165         
14166         //this.trigger.addClassOnOver('x-form-trigger-over');
14167         //this.trigger.addClassOnClick('x-form-trigger-click');
14168         
14169         //if(!this.width){
14170         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14171         //}
14172     },
14173     
14174     closeTriggerEl : function()
14175     {
14176         var close = this.el.select('.roo-combo-removable-btn', true).first();
14177         return close ? close : false;
14178     },
14179     
14180     removeBtnClick : function(e, h, el)
14181     {
14182         e.preventDefault();
14183         
14184         if(this.fireEvent("remove", this) !== false){
14185             this.reset();
14186             this.fireEvent("afterremove", this)
14187         }
14188     },
14189     
14190     createList : function()
14191     {
14192         this.list = Roo.get(document.body).createChild({
14193             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14194             cls: 'typeahead typeahead-long dropdown-menu shadow',
14195             style: 'display:none'
14196         });
14197         
14198         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14199         
14200     },
14201
14202     // private
14203     initTrigger : function(){
14204        
14205     },
14206
14207     // private
14208     onDestroy : function(){
14209         if(this.trigger){
14210             this.trigger.removeAllListeners();
14211           //  this.trigger.remove();
14212         }
14213         //if(this.wrap){
14214         //    this.wrap.remove();
14215         //}
14216         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14217     },
14218
14219     // private
14220     onFocus : function(){
14221         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14222         /*
14223         if(!this.mimicing){
14224             this.wrap.addClass('x-trigger-wrap-focus');
14225             this.mimicing = true;
14226             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14227             if(this.monitorTab){
14228                 this.el.on("keydown", this.checkTab, this);
14229             }
14230         }
14231         */
14232     },
14233
14234     // private
14235     checkTab : function(e){
14236         if(e.getKey() == e.TAB){
14237             this.triggerBlur();
14238         }
14239     },
14240
14241     // private
14242     onBlur : function(){
14243         // do nothing
14244     },
14245
14246     // private
14247     mimicBlur : function(e, t){
14248         /*
14249         if(!this.wrap.contains(t) && this.validateBlur()){
14250             this.triggerBlur();
14251         }
14252         */
14253     },
14254
14255     // private
14256     triggerBlur : function(){
14257         this.mimicing = false;
14258         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14259         if(this.monitorTab){
14260             this.el.un("keydown", this.checkTab, this);
14261         }
14262         //this.wrap.removeClass('x-trigger-wrap-focus');
14263         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14264     },
14265
14266     // private
14267     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14268     validateBlur : function(e, t){
14269         return true;
14270     },
14271
14272     // private
14273     onDisable : function(){
14274         this.inputEl().dom.disabled = true;
14275         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14276         //if(this.wrap){
14277         //    this.wrap.addClass('x-item-disabled');
14278         //}
14279     },
14280
14281     // private
14282     onEnable : function(){
14283         this.inputEl().dom.disabled = false;
14284         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14285         //if(this.wrap){
14286         //    this.el.removeClass('x-item-disabled');
14287         //}
14288     },
14289
14290     // private
14291     onShow : function(){
14292         var ae = this.getActionEl();
14293         
14294         if(ae){
14295             ae.dom.style.display = '';
14296             ae.dom.style.visibility = 'visible';
14297         }
14298     },
14299
14300     // private
14301     
14302     onHide : function(){
14303         var ae = this.getActionEl();
14304         ae.dom.style.display = 'none';
14305     },
14306
14307     /**
14308      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14309      * by an implementing function.
14310      * @method
14311      * @param {EventObject} e
14312      */
14313     onTriggerClick : Roo.emptyFn
14314 });
14315  
14316 /*
14317 * Licence: LGPL
14318 */
14319
14320 /**
14321  * @class Roo.bootstrap.form.CardUploader
14322  * @extends Roo.bootstrap.Button
14323  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14324  * @cfg {Number} errorTimeout default 3000
14325  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14326  * @cfg {Array}  html The button text.
14327
14328  *
14329  * @constructor
14330  * Create a new CardUploader
14331  * @param {Object} config The config object
14332  */
14333
14334 Roo.bootstrap.form.CardUploader = function(config){
14335     
14336  
14337     
14338     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14339     
14340     
14341     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14342         return r.data.id
14343      });
14344     
14345      this.addEvents({
14346          // raw events
14347         /**
14348          * @event preview
14349          * When a image is clicked on - and needs to display a slideshow or similar..
14350          * @param {Roo.bootstrap.Card} this
14351          * @param {Object} The image information data 
14352          *
14353          */
14354         'preview' : true,
14355          /**
14356          * @event download
14357          * When a the download link is clicked
14358          * @param {Roo.bootstrap.Card} this
14359          * @param {Object} The image information data  contains 
14360          */
14361         'download' : true
14362         
14363     });
14364 };
14365  
14366 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14367     
14368      
14369     errorTimeout : 3000,
14370      
14371     images : false,
14372    
14373     fileCollection : false,
14374     allowBlank : true,
14375     
14376     getAutoCreate : function()
14377     {
14378         
14379         var cfg =  {
14380             cls :'form-group' ,
14381             cn : [
14382                
14383                 {
14384                     tag: 'label',
14385                    //cls : 'input-group-addon',
14386                     html : this.fieldLabel
14387
14388                 },
14389
14390                 {
14391                     tag: 'input',
14392                     type : 'hidden',
14393                     name : this.name,
14394                     value : this.value,
14395                     cls : 'd-none  form-control'
14396                 },
14397                 
14398                 {
14399                     tag: 'input',
14400                     multiple : 'multiple',
14401                     type : 'file',
14402                     cls : 'd-none  roo-card-upload-selector'
14403                 },
14404                 
14405                 {
14406                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14407                 },
14408                 {
14409                     cls : 'card-columns roo-card-uploader-container'
14410                 }
14411
14412             ]
14413         };
14414            
14415          
14416         return cfg;
14417     },
14418     
14419     getChildContainer : function() /// what children are added to.
14420     {
14421         return this.containerEl;
14422     },
14423    
14424     getButtonContainer : function() /// what children are added to.
14425     {
14426         return this.el.select(".roo-card-uploader-button-container").first();
14427     },
14428    
14429     initEvents : function()
14430     {
14431         
14432         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14433         
14434         var t = this;
14435         this.addxtype({
14436             xns: Roo.bootstrap,
14437
14438             xtype : 'Button',
14439             container_method : 'getButtonContainer' ,            
14440             html :  this.html, // fix changable?
14441             cls : 'w-100 ',
14442             listeners : {
14443                 'click' : function(btn, e) {
14444                     t.onClick(e);
14445                 }
14446             }
14447         });
14448         
14449         
14450         
14451         
14452         this.urlAPI = (window.createObjectURL && window) || 
14453                                 (window.URL && URL.revokeObjectURL && URL) || 
14454                                 (window.webkitURL && webkitURL);
14455                         
14456          
14457          
14458          
14459         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14460         
14461         this.selectorEl.on('change', this.onFileSelected, this);
14462         if (this.images) {
14463             var t = this;
14464             this.images.forEach(function(img) {
14465                 t.addCard(img)
14466             });
14467             this.images = false;
14468         }
14469         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470          
14471        
14472     },
14473     
14474    
14475     onClick : function(e)
14476     {
14477         e.preventDefault();
14478          
14479         this.selectorEl.dom.click();
14480          
14481     },
14482     
14483     onFileSelected : function(e)
14484     {
14485         e.preventDefault();
14486         
14487         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14488             return;
14489         }
14490         
14491         Roo.each(this.selectorEl.dom.files, function(file){    
14492             this.addFile(file);
14493         }, this);
14494          
14495     },
14496     
14497       
14498     
14499       
14500     
14501     addFile : function(file)
14502     {
14503            
14504         if(typeof(file) === 'string'){
14505             throw "Add file by name?"; // should not happen
14506             return;
14507         }
14508         
14509         if(!file || !this.urlAPI){
14510             return;
14511         }
14512         
14513         // file;
14514         // file.type;
14515         
14516         var _this = this;
14517         
14518         
14519         var url = _this.urlAPI.createObjectURL( file);
14520            
14521         this.addCard({
14522             id : Roo.bootstrap.form.CardUploader.ID--,
14523             is_uploaded : false,
14524             src : url,
14525             srcfile : file,
14526             title : file.name,
14527             mimetype : file.type,
14528             preview : false,
14529             is_deleted : 0
14530         });
14531         
14532     },
14533     
14534     /**
14535      * addCard - add an Attachment to the uploader
14536      * @param data - the data about the image to upload
14537      *
14538      * {
14539           id : 123
14540           title : "Title of file",
14541           is_uploaded : false,
14542           src : "http://.....",
14543           srcfile : { the File upload object },
14544           mimetype : file.type,
14545           preview : false,
14546           is_deleted : 0
14547           .. any other data...
14548         }
14549      *
14550      * 
14551     */
14552     
14553     addCard : function (data)
14554     {
14555         // hidden input element?
14556         // if the file is not an image...
14557         //then we need to use something other that and header_image
14558         var t = this;
14559         //   remove.....
14560         var footer = [
14561             {
14562                 xns : Roo.bootstrap,
14563                 xtype : 'CardFooter',
14564                  items: [
14565                     {
14566                         xns : Roo.bootstrap,
14567                         xtype : 'Element',
14568                         cls : 'd-flex',
14569                         items : [
14570                             
14571                             {
14572                                 xns : Roo.bootstrap,
14573                                 xtype : 'Button',
14574                                 html : String.format("<small>{0}</small>", data.title),
14575                                 cls : 'col-10 text-left',
14576                                 size: 'sm',
14577                                 weight: 'link',
14578                                 fa : 'download',
14579                                 listeners : {
14580                                     click : function() {
14581                                      
14582                                         t.fireEvent( "download", t, data );
14583                                     }
14584                                 }
14585                             },
14586                           
14587                             {
14588                                 xns : Roo.bootstrap,
14589                                 xtype : 'Button',
14590                                 style: 'max-height: 28px; ',
14591                                 size : 'sm',
14592                                 weight: 'danger',
14593                                 cls : 'col-2',
14594                                 fa : 'times',
14595                                 listeners : {
14596                                     click : function() {
14597                                         t.removeCard(data.id)
14598                                     }
14599                                 }
14600                             }
14601                         ]
14602                     }
14603                     
14604                 ] 
14605             }
14606             
14607         ];
14608         
14609         var cn = this.addxtype(
14610             {
14611                  
14612                 xns : Roo.bootstrap,
14613                 xtype : 'Card',
14614                 closeable : true,
14615                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14616                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14617                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14618                 data : data,
14619                 html : false,
14620                  
14621                 items : footer,
14622                 initEvents : function() {
14623                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14624                     var card = this;
14625                     this.imgEl = this.el.select('.card-img-top').first();
14626                     if (this.imgEl) {
14627                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14628                         this.imgEl.set({ 'pointer' : 'cursor' });
14629                                   
14630                     }
14631                     this.getCardFooter().addClass('p-1');
14632                     
14633                   
14634                 }
14635                 
14636             }
14637         );
14638         // dont' really need ot update items.
14639         // this.items.push(cn);
14640         this.fileCollection.add(cn);
14641         
14642         if (!data.srcfile) {
14643             this.updateInput();
14644             return;
14645         }
14646             
14647         var _t = this;
14648         var reader = new FileReader();
14649         reader.addEventListener("load", function() {  
14650             data.srcdata =  reader.result;
14651             _t.updateInput();
14652         });
14653         reader.readAsDataURL(data.srcfile);
14654         
14655         
14656         
14657     },
14658     removeCard : function(id)
14659     {
14660         
14661         var card  = this.fileCollection.get(id);
14662         card.data.is_deleted = 1;
14663         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14664         //this.fileCollection.remove(card);
14665         //this.items = this.items.filter(function(e) { return e != card });
14666         // dont' really need ot update items.
14667         card.el.dom.parentNode.removeChild(card.el.dom);
14668         this.updateInput();
14669
14670         
14671     },
14672     reset: function()
14673     {
14674         this.fileCollection.each(function(card) {
14675             if (card.el.dom && card.el.dom.parentNode) {
14676                 card.el.dom.parentNode.removeChild(card.el.dom);
14677             }
14678         });
14679         this.fileCollection.clear();
14680         this.updateInput();
14681     },
14682     
14683     updateInput : function()
14684     {
14685          var data = [];
14686         this.fileCollection.each(function(e) {
14687             data.push(e.data);
14688             
14689         });
14690         this.inputEl().dom.value = JSON.stringify(data);
14691         
14692         
14693         
14694     }
14695     
14696     
14697 });
14698
14699
14700 Roo.bootstrap.form.CardUploader.ID = -1;/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711
14712 /**
14713  * @class Roo.data.SortTypes
14714  * @static
14715  * Defines the default sorting (casting?) comparison functions used when sorting data.
14716  */
14717 Roo.data.SortTypes = {
14718     /**
14719      * Default sort that does nothing
14720      * @param {Mixed} s The value being converted
14721      * @return {Mixed} The comparison value
14722      */
14723     none : function(s){
14724         return s;
14725     },
14726     
14727     /**
14728      * The regular expression used to strip tags
14729      * @type {RegExp}
14730      * @property
14731      */
14732     stripTagsRE : /<\/?[^>]+>/gi,
14733     
14734     /**
14735      * Strips all HTML tags to sort on text only
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asText : function(s){
14740         return String(s).replace(this.stripTagsRE, "");
14741     },
14742     
14743     /**
14744      * Strips all HTML tags to sort on text only - Case insensitive
14745      * @param {Mixed} s The value being converted
14746      * @return {String} The comparison value
14747      */
14748     asUCText : function(s){
14749         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14750     },
14751     
14752     /**
14753      * Case insensitive string
14754      * @param {Mixed} s The value being converted
14755      * @return {String} The comparison value
14756      */
14757     asUCString : function(s) {
14758         return String(s).toUpperCase();
14759     },
14760     
14761     /**
14762      * Date sorting
14763      * @param {Mixed} s The value being converted
14764      * @return {Number} The comparison value
14765      */
14766     asDate : function(s) {
14767         if(!s){
14768             return 0;
14769         }
14770         if(s instanceof Date){
14771             return s.getTime();
14772         }
14773         return Date.parse(String(s));
14774     },
14775     
14776     /**
14777      * Float sorting
14778      * @param {Mixed} s The value being converted
14779      * @return {Float} The comparison value
14780      */
14781     asFloat : function(s) {
14782         var val = parseFloat(String(s).replace(/,/g, ""));
14783         if(isNaN(val)) {
14784             val = 0;
14785         }
14786         return val;
14787     },
14788     
14789     /**
14790      * Integer sorting
14791      * @param {Mixed} s The value being converted
14792      * @return {Number} The comparison value
14793      */
14794     asInt : function(s) {
14795         var val = parseInt(String(s).replace(/,/g, ""));
14796         if(isNaN(val)) {
14797             val = 0;
14798         }
14799         return val;
14800     }
14801 };/*
14802  * Based on:
14803  * Ext JS Library 1.1.1
14804  * Copyright(c) 2006-2007, Ext JS, LLC.
14805  *
14806  * Originally Released Under LGPL - original licence link has changed is not relivant.
14807  *
14808  * Fork - LGPL
14809  * <script type="text/javascript">
14810  */
14811
14812 /**
14813 * @class Roo.data.Record
14814  * Instances of this class encapsulate both record <em>definition</em> information, and record
14815  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14816  * to access Records cached in an {@link Roo.data.Store} object.<br>
14817  * <p>
14818  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14819  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14820  * objects.<br>
14821  * <p>
14822  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14823  * @constructor
14824  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14825  * {@link #create}. The parameters are the same.
14826  * @param {Array} data An associative Array of data values keyed by the field name.
14827  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14828  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14829  * not specified an integer id is generated.
14830  */
14831 Roo.data.Record = function(data, id){
14832     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14833     this.data = data;
14834 };
14835
14836 /**
14837  * Generate a constructor for a specific record layout.
14838  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14839  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14840  * Each field definition object may contain the following properties: <ul>
14841  * <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,
14842  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14843  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14844  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14845  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14846  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14847  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14848  * this may be omitted.</p></li>
14849  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14850  * <ul><li>auto (Default, implies no conversion)</li>
14851  * <li>string</li>
14852  * <li>int</li>
14853  * <li>float</li>
14854  * <li>boolean</li>
14855  * <li>date</li></ul></p></li>
14856  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14857  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14858  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14859  * by the Reader into an object that will be stored in the Record. It is passed the
14860  * following parameters:<ul>
14861  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14862  * </ul></p></li>
14863  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14864  * </ul>
14865  * <br>usage:<br><pre><code>
14866 var TopicRecord = Roo.data.Record.create(
14867     {name: 'title', mapping: 'topic_title'},
14868     {name: 'author', mapping: 'username'},
14869     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14870     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14871     {name: 'lastPoster', mapping: 'user2'},
14872     {name: 'excerpt', mapping: 'post_text'}
14873 );
14874
14875 var myNewRecord = new TopicRecord({
14876     title: 'Do my job please',
14877     author: 'noobie',
14878     totalPosts: 1,
14879     lastPost: new Date(),
14880     lastPoster: 'Animal',
14881     excerpt: 'No way dude!'
14882 });
14883 myStore.add(myNewRecord);
14884 </code></pre>
14885  * @method create
14886  * @static
14887  */
14888 Roo.data.Record.create = function(o){
14889     var f = function(){
14890         f.superclass.constructor.apply(this, arguments);
14891     };
14892     Roo.extend(f, Roo.data.Record);
14893     var p = f.prototype;
14894     p.fields = new Roo.util.MixedCollection(false, function(field){
14895         return field.name;
14896     });
14897     for(var i = 0, len = o.length; i < len; i++){
14898         p.fields.add(new Roo.data.Field(o[i]));
14899     }
14900     f.getField = function(name){
14901         return p.fields.get(name);  
14902     };
14903     return f;
14904 };
14905
14906 Roo.data.Record.AUTO_ID = 1000;
14907 Roo.data.Record.EDIT = 'edit';
14908 Roo.data.Record.REJECT = 'reject';
14909 Roo.data.Record.COMMIT = 'commit';
14910
14911 Roo.data.Record.prototype = {
14912     /**
14913      * Readonly flag - true if this record has been modified.
14914      * @type Boolean
14915      */
14916     dirty : false,
14917     editing : false,
14918     error: null,
14919     modified: null,
14920
14921     // private
14922     join : function(store){
14923         this.store = store;
14924     },
14925
14926     /**
14927      * Set the named field to the specified value.
14928      * @param {String} name The name of the field to set.
14929      * @param {Object} value The value to set the field to.
14930      */
14931     set : function(name, value){
14932         if(this.data[name] == value){
14933             return;
14934         }
14935         this.dirty = true;
14936         if(!this.modified){
14937             this.modified = {};
14938         }
14939         if(typeof this.modified[name] == 'undefined'){
14940             this.modified[name] = this.data[name];
14941         }
14942         this.data[name] = value;
14943         if(!this.editing && this.store){
14944             this.store.afterEdit(this);
14945         }       
14946     },
14947
14948     /**
14949      * Get the value of the named field.
14950      * @param {String} name The name of the field to get the value of.
14951      * @return {Object} The value of the field.
14952      */
14953     get : function(name){
14954         return this.data[name]; 
14955     },
14956
14957     // private
14958     beginEdit : function(){
14959         this.editing = true;
14960         this.modified = {}; 
14961     },
14962
14963     // private
14964     cancelEdit : function(){
14965         this.editing = false;
14966         delete this.modified;
14967     },
14968
14969     // private
14970     endEdit : function(){
14971         this.editing = false;
14972         if(this.dirty && this.store){
14973             this.store.afterEdit(this);
14974         }
14975     },
14976
14977     /**
14978      * Usually called by the {@link Roo.data.Store} which owns the Record.
14979      * Rejects all changes made to the Record since either creation, or the last commit operation.
14980      * Modified fields are reverted to their original values.
14981      * <p>
14982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14983      * of reject operations.
14984      */
14985     reject : function(){
14986         var m = this.modified;
14987         for(var n in m){
14988             if(typeof m[n] != "function"){
14989                 this.data[n] = m[n];
14990             }
14991         }
14992         this.dirty = false;
14993         delete this.modified;
14994         this.editing = false;
14995         if(this.store){
14996             this.store.afterReject(this);
14997         }
14998     },
14999
15000     /**
15001      * Usually called by the {@link Roo.data.Store} which owns the Record.
15002      * Commits all changes made to the Record since either creation, or the last commit operation.
15003      * <p>
15004      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15005      * of commit operations.
15006      */
15007     commit : function(){
15008         this.dirty = false;
15009         delete this.modified;
15010         this.editing = false;
15011         if(this.store){
15012             this.store.afterCommit(this);
15013         }
15014     },
15015
15016     // private
15017     hasError : function(){
15018         return this.error != null;
15019     },
15020
15021     // private
15022     clearError : function(){
15023         this.error = null;
15024     },
15025
15026     /**
15027      * Creates a copy of this record.
15028      * @param {String} id (optional) A new record id if you don't want to use this record's id
15029      * @return {Record}
15030      */
15031     copy : function(newId) {
15032         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15033     }
15034 };/*
15035  * Based on:
15036  * Ext JS Library 1.1.1
15037  * Copyright(c) 2006-2007, Ext JS, LLC.
15038  *
15039  * Originally Released Under LGPL - original licence link has changed is not relivant.
15040  *
15041  * Fork - LGPL
15042  * <script type="text/javascript">
15043  */
15044
15045
15046
15047 /**
15048  * @class Roo.data.Store
15049  * @extends Roo.util.Observable
15050  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15051  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15052  * <p>
15053  * 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
15054  * has no knowledge of the format of the data returned by the Proxy.<br>
15055  * <p>
15056  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15057  * instances from the data object. These records are cached and made available through accessor functions.
15058  * @constructor
15059  * Creates a new Store.
15060  * @param {Object} config A config object containing the objects needed for the Store to access data,
15061  * and read the data into Records.
15062  */
15063 Roo.data.Store = function(config){
15064     this.data = new Roo.util.MixedCollection(false);
15065     this.data.getKey = function(o){
15066         return o.id;
15067     };
15068     this.baseParams = {};
15069     // private
15070     this.paramNames = {
15071         "start" : "start",
15072         "limit" : "limit",
15073         "sort" : "sort",
15074         "dir" : "dir",
15075         "multisort" : "_multisort"
15076     };
15077
15078     if(config && config.data){
15079         this.inlineData = config.data;
15080         delete config.data;
15081     }
15082
15083     Roo.apply(this, config);
15084     
15085     if(this.reader){ // reader passed
15086         this.reader = Roo.factory(this.reader, Roo.data);
15087         this.reader.xmodule = this.xmodule || false;
15088         if(!this.recordType){
15089             this.recordType = this.reader.recordType;
15090         }
15091         if(this.reader.onMetaChange){
15092             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15093         }
15094     }
15095
15096     if(this.recordType){
15097         this.fields = this.recordType.prototype.fields;
15098     }
15099     this.modified = [];
15100
15101     this.addEvents({
15102         /**
15103          * @event datachanged
15104          * Fires when the data cache has changed, and a widget which is using this Store
15105          * as a Record cache should refresh its view.
15106          * @param {Store} this
15107          */
15108         datachanged : true,
15109         /**
15110          * @event metachange
15111          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15112          * @param {Store} this
15113          * @param {Object} meta The JSON metadata
15114          */
15115         metachange : true,
15116         /**
15117          * @event add
15118          * Fires when Records have been added to the Store
15119          * @param {Store} this
15120          * @param {Roo.data.Record[]} records The array of Records added
15121          * @param {Number} index The index at which the record(s) were added
15122          */
15123         add : true,
15124         /**
15125          * @event remove
15126          * Fires when a Record has been removed from the Store
15127          * @param {Store} this
15128          * @param {Roo.data.Record} record The Record that was removed
15129          * @param {Number} index The index at which the record was removed
15130          */
15131         remove : true,
15132         /**
15133          * @event update
15134          * Fires when a Record has been updated
15135          * @param {Store} this
15136          * @param {Roo.data.Record} record The Record that was updated
15137          * @param {String} operation The update operation being performed.  Value may be one of:
15138          * <pre><code>
15139  Roo.data.Record.EDIT
15140  Roo.data.Record.REJECT
15141  Roo.data.Record.COMMIT
15142          * </code></pre>
15143          */
15144         update : true,
15145         /**
15146          * @event clear
15147          * Fires when the data cache has been cleared.
15148          * @param {Store} this
15149          */
15150         clear : true,
15151         /**
15152          * @event beforeload
15153          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15154          * the load action will be canceled.
15155          * @param {Store} this
15156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15157          */
15158         beforeload : true,
15159         /**
15160          * @event beforeloadadd
15161          * Fires after a new set of Records has been loaded.
15162          * @param {Store} this
15163          * @param {Roo.data.Record[]} records The Records that were loaded
15164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165          */
15166         beforeloadadd : true,
15167         /**
15168          * @event load
15169          * Fires after a new set of Records has been loaded, before they are added to the store.
15170          * @param {Store} this
15171          * @param {Roo.data.Record[]} records The Records that were loaded
15172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15173          * @params {Object} return from reader
15174          */
15175         load : true,
15176         /**
15177          * @event loadexception
15178          * Fires if an exception occurs in the Proxy during loading.
15179          * Called with the signature of the Proxy's "loadexception" event.
15180          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15181          * 
15182          * @param {Proxy} 
15183          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15184          * @param {Object} load options 
15185          * @param {Object} jsonData from your request (normally this contains the Exception)
15186          */
15187         loadexception : true
15188     });
15189     
15190     if(this.proxy){
15191         this.proxy = Roo.factory(this.proxy, Roo.data);
15192         this.proxy.xmodule = this.xmodule || false;
15193         this.relayEvents(this.proxy,  ["loadexception"]);
15194     }
15195     this.sortToggle = {};
15196     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15197
15198     Roo.data.Store.superclass.constructor.call(this);
15199
15200     if(this.inlineData){
15201         this.loadData(this.inlineData);
15202         delete this.inlineData;
15203     }
15204 };
15205
15206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15207      /**
15208     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15209     * without a remote query - used by combo/forms at present.
15210     */
15211     
15212     /**
15213     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15214     */
15215     /**
15216     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15217     */
15218     /**
15219     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15220     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15221     */
15222     /**
15223     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15224     * on any HTTP request
15225     */
15226     /**
15227     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15228     */
15229     /**
15230     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15231     */
15232     multiSort: false,
15233     /**
15234     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15235     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15236     */
15237     remoteSort : false,
15238
15239     /**
15240     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15241      * loaded or when a record is removed. (defaults to false).
15242     */
15243     pruneModifiedRecords : false,
15244
15245     // private
15246     lastOptions : null,
15247
15248     /**
15249      * Add Records to the Store and fires the add event.
15250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15251      */
15252     add : function(records){
15253         records = [].concat(records);
15254         for(var i = 0, len = records.length; i < len; i++){
15255             records[i].join(this);
15256         }
15257         var index = this.data.length;
15258         this.data.addAll(records);
15259         this.fireEvent("add", this, records, index);
15260     },
15261
15262     /**
15263      * Remove a Record from the Store and fires the remove event.
15264      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15265      */
15266     remove : function(record){
15267         var index = this.data.indexOf(record);
15268         this.data.removeAt(index);
15269  
15270         if(this.pruneModifiedRecords){
15271             this.modified.remove(record);
15272         }
15273         this.fireEvent("remove", this, record, index);
15274     },
15275
15276     /**
15277      * Remove all Records from the Store and fires the clear event.
15278      */
15279     removeAll : function(){
15280         this.data.clear();
15281         if(this.pruneModifiedRecords){
15282             this.modified = [];
15283         }
15284         this.fireEvent("clear", this);
15285     },
15286
15287     /**
15288      * Inserts Records to the Store at the given index and fires the add event.
15289      * @param {Number} index The start index at which to insert the passed Records.
15290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15291      */
15292     insert : function(index, records){
15293         records = [].concat(records);
15294         for(var i = 0, len = records.length; i < len; i++){
15295             this.data.insert(index, records[i]);
15296             records[i].join(this);
15297         }
15298         this.fireEvent("add", this, records, index);
15299     },
15300
15301     /**
15302      * Get the index within the cache of the passed Record.
15303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15304      * @return {Number} The index of the passed Record. Returns -1 if not found.
15305      */
15306     indexOf : function(record){
15307         return this.data.indexOf(record);
15308     },
15309
15310     /**
15311      * Get the index within the cache of the Record with the passed id.
15312      * @param {String} id The id of the Record to find.
15313      * @return {Number} The index of the Record. Returns -1 if not found.
15314      */
15315     indexOfId : function(id){
15316         return this.data.indexOfKey(id);
15317     },
15318
15319     /**
15320      * Get the Record with the specified id.
15321      * @param {String} id The id of the Record to find.
15322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15323      */
15324     getById : function(id){
15325         return this.data.key(id);
15326     },
15327
15328     /**
15329      * Get the Record at the specified index.
15330      * @param {Number} index The index of the Record to find.
15331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15332      */
15333     getAt : function(index){
15334         return this.data.itemAt(index);
15335     },
15336
15337     /**
15338      * Returns a range of Records between specified indices.
15339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15341      * @return {Roo.data.Record[]} An array of Records
15342      */
15343     getRange : function(start, end){
15344         return this.data.getRange(start, end);
15345     },
15346
15347     // private
15348     storeOptions : function(o){
15349         o = Roo.apply({}, o);
15350         delete o.callback;
15351         delete o.scope;
15352         this.lastOptions = o;
15353     },
15354
15355     /**
15356      * Loads the Record cache from the configured Proxy using the configured Reader.
15357      * <p>
15358      * If using remote paging, then the first load call must specify the <em>start</em>
15359      * and <em>limit</em> properties in the options.params property to establish the initial
15360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15361      * <p>
15362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15363      * and this call will return before the new data has been loaded. Perform any post-processing
15364      * in a callback function, or in a "load" event handler.</strong>
15365      * <p>
15366      * @param {Object} options An object containing properties which control loading options:<ul>
15367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15368      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15369      * <pre>
15370                 {
15371                     data : data,  // array of key=>value data like JsonReader
15372                     total : data.length,
15373                     success : true
15374                     
15375                 }
15376         </pre>
15377             }.</li>
15378      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15379      * passed the following arguments:<ul>
15380      * <li>r : Roo.data.Record[]</li>
15381      * <li>options: Options object from the load call</li>
15382      * <li>success: Boolean success indicator</li></ul></li>
15383      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15384      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15385      * </ul>
15386      */
15387     load : function(options){
15388         options = options || {};
15389         if(this.fireEvent("beforeload", this, options) !== false){
15390             this.storeOptions(options);
15391             var p = Roo.apply(options.params || {}, this.baseParams);
15392             // if meta was not loaded from remote source.. try requesting it.
15393             if (!this.reader.metaFromRemote) {
15394                 p._requestMeta = 1;
15395             }
15396             if(this.sortInfo && this.remoteSort){
15397                 var pn = this.paramNames;
15398                 p[pn["sort"]] = this.sortInfo.field;
15399                 p[pn["dir"]] = this.sortInfo.direction;
15400             }
15401             if (this.multiSort) {
15402                 var pn = this.paramNames;
15403                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15404             }
15405             
15406             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15407         }
15408     },
15409
15410     /**
15411      * Reloads the Record cache from the configured Proxy using the configured Reader and
15412      * the options from the last load operation performed.
15413      * @param {Object} options (optional) An object containing properties which may override the options
15414      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15415      * the most recently used options are reused).
15416      */
15417     reload : function(options){
15418         this.load(Roo.applyIf(options||{}, this.lastOptions));
15419     },
15420
15421     // private
15422     // Called as a callback by the Reader during a load operation.
15423     loadRecords : function(o, options, success){
15424          
15425         if(!o){
15426             if(success !== false){
15427                 this.fireEvent("load", this, [], options, o);
15428             }
15429             if(options.callback){
15430                 options.callback.call(options.scope || this, [], options, false);
15431             }
15432             return;
15433         }
15434         // if data returned failure - throw an exception.
15435         if (o.success === false) {
15436             // show a message if no listener is registered.
15437             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15438                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15439             }
15440             // loadmask wil be hooked into this..
15441             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15442             return;
15443         }
15444         var r = o.records, t = o.totalRecords || r.length;
15445         
15446         this.fireEvent("beforeloadadd", this, r, options, o);
15447         
15448         if(!options || options.add !== true){
15449             if(this.pruneModifiedRecords){
15450                 this.modified = [];
15451             }
15452             for(var i = 0, len = r.length; i < len; i++){
15453                 r[i].join(this);
15454             }
15455             if(this.snapshot){
15456                 this.data = this.snapshot;
15457                 delete this.snapshot;
15458             }
15459             this.data.clear();
15460             this.data.addAll(r);
15461             this.totalLength = t;
15462             this.applySort();
15463             this.fireEvent("datachanged", this);
15464         }else{
15465             this.totalLength = Math.max(t, this.data.length+r.length);
15466             this.add(r);
15467         }
15468         
15469         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15470                 
15471             var e = new Roo.data.Record({});
15472
15473             e.set(this.parent.displayField, this.parent.emptyTitle);
15474             e.set(this.parent.valueField, '');
15475
15476             this.insert(0, e);
15477         }
15478             
15479         this.fireEvent("load", this, r, options, o);
15480         if(options.callback){
15481             options.callback.call(options.scope || this, r, options, true);
15482         }
15483     },
15484
15485
15486     /**
15487      * Loads data from a passed data block. A Reader which understands the format of the data
15488      * must have been configured in the constructor.
15489      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15490      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15491      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15492      */
15493     loadData : function(o, append){
15494         var r = this.reader.readRecords(o);
15495         this.loadRecords(r, {add: append}, true);
15496     },
15497     
15498      /**
15499      * using 'cn' the nested child reader read the child array into it's child stores.
15500      * @param {Object} rec The record with a 'children array
15501      */
15502     loadDataFromChildren : function(rec)
15503     {
15504         this.loadData(this.reader.toLoadData(rec));
15505     },
15506     
15507
15508     /**
15509      * Gets the number of cached records.
15510      * <p>
15511      * <em>If using paging, this may not be the total size of the dataset. If the data object
15512      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15513      * the data set size</em>
15514      */
15515     getCount : function(){
15516         return this.data.length || 0;
15517     },
15518
15519     /**
15520      * Gets the total number of records in the dataset as returned by the server.
15521      * <p>
15522      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15523      * the dataset size</em>
15524      */
15525     getTotalCount : function(){
15526         return this.totalLength || 0;
15527     },
15528
15529     /**
15530      * Returns the sort state of the Store as an object with two properties:
15531      * <pre><code>
15532  field {String} The name of the field by which the Records are sorted
15533  direction {String} The sort order, "ASC" or "DESC"
15534      * </code></pre>
15535      */
15536     getSortState : function(){
15537         return this.sortInfo;
15538     },
15539
15540     // private
15541     applySort : function(){
15542         if(this.sortInfo && !this.remoteSort){
15543             var s = this.sortInfo, f = s.field;
15544             var st = this.fields.get(f).sortType;
15545             var fn = function(r1, r2){
15546                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15547                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15548             };
15549             this.data.sort(s.direction, fn);
15550             if(this.snapshot && this.snapshot != this.data){
15551                 this.snapshot.sort(s.direction, fn);
15552             }
15553         }
15554     },
15555
15556     /**
15557      * Sets the default sort column and order to be used by the next load operation.
15558      * @param {String} fieldName The name of the field to sort by.
15559      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15560      */
15561     setDefaultSort : function(field, dir){
15562         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15563     },
15564
15565     /**
15566      * Sort the Records.
15567      * If remote sorting is used, the sort is performed on the server, and the cache is
15568      * reloaded. If local sorting is used, the cache is sorted internally.
15569      * @param {String} fieldName The name of the field to sort by.
15570      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15571      */
15572     sort : function(fieldName, dir){
15573         var f = this.fields.get(fieldName);
15574         if(!dir){
15575             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15576             
15577             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15578                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15579             }else{
15580                 dir = f.sortDir;
15581             }
15582         }
15583         this.sortToggle[f.name] = dir;
15584         this.sortInfo = {field: f.name, direction: dir};
15585         if(!this.remoteSort){
15586             this.applySort();
15587             this.fireEvent("datachanged", this);
15588         }else{
15589             this.load(this.lastOptions);
15590         }
15591     },
15592
15593     /**
15594      * Calls the specified function for each of the Records in the cache.
15595      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15596      * Returning <em>false</em> aborts and exits the iteration.
15597      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15598      */
15599     each : function(fn, scope){
15600         this.data.each(fn, scope);
15601     },
15602
15603     /**
15604      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15605      * (e.g., during paging).
15606      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15607      */
15608     getModifiedRecords : function(){
15609         return this.modified;
15610     },
15611
15612     // private
15613     createFilterFn : function(property, value, anyMatch){
15614         if(!value.exec){ // not a regex
15615             value = String(value);
15616             if(value.length == 0){
15617                 return false;
15618             }
15619             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15620         }
15621         return function(r){
15622             return value.test(r.data[property]);
15623         };
15624     },
15625
15626     /**
15627      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15628      * @param {String} property A field on your records
15629      * @param {Number} start The record index to start at (defaults to 0)
15630      * @param {Number} end The last record index to include (defaults to length - 1)
15631      * @return {Number} The sum
15632      */
15633     sum : function(property, start, end){
15634         var rs = this.data.items, v = 0;
15635         start = start || 0;
15636         end = (end || end === 0) ? end : rs.length-1;
15637
15638         for(var i = start; i <= end; i++){
15639             v += (rs[i].data[property] || 0);
15640         }
15641         return v;
15642     },
15643
15644     /**
15645      * Filter the records by a specified property.
15646      * @param {String} field A field on your records
15647      * @param {String/RegExp} value Either a string that the field
15648      * should start with or a RegExp to test against the field
15649      * @param {Boolean} anyMatch True to match any part not just the beginning
15650      */
15651     filter : function(property, value, anyMatch){
15652         var fn = this.createFilterFn(property, value, anyMatch);
15653         return fn ? this.filterBy(fn) : this.clearFilter();
15654     },
15655
15656     /**
15657      * Filter by a function. The specified function will be called with each
15658      * record in this data source. If the function returns true the record is included,
15659      * otherwise it is filtered.
15660      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15661      * @param {Object} scope (optional) The scope of the function (defaults to this)
15662      */
15663     filterBy : function(fn, scope){
15664         this.snapshot = this.snapshot || this.data;
15665         this.data = this.queryBy(fn, scope||this);
15666         this.fireEvent("datachanged", this);
15667     },
15668
15669     /**
15670      * Query the records by a specified property.
15671      * @param {String} field A field on your records
15672      * @param {String/RegExp} value Either a string that the field
15673      * should start with or a RegExp to test against the field
15674      * @param {Boolean} anyMatch True to match any part not just the beginning
15675      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15676      */
15677     query : function(property, value, anyMatch){
15678         var fn = this.createFilterFn(property, value, anyMatch);
15679         return fn ? this.queryBy(fn) : this.data.clone();
15680     },
15681
15682     /**
15683      * Query by a function. The specified function will be called with each
15684      * record in this data source. If the function returns true the record is included
15685      * in the results.
15686      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687      * @param {Object} scope (optional) The scope of the function (defaults to this)
15688       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15689      **/
15690     queryBy : function(fn, scope){
15691         var data = this.snapshot || this.data;
15692         return data.filterBy(fn, scope||this);
15693     },
15694
15695     /**
15696      * Collects unique values for a particular dataIndex from this store.
15697      * @param {String} dataIndex The property to collect
15698      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15699      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15700      * @return {Array} An array of the unique values
15701      **/
15702     collect : function(dataIndex, allowNull, bypassFilter){
15703         var d = (bypassFilter === true && this.snapshot) ?
15704                 this.snapshot.items : this.data.items;
15705         var v, sv, r = [], l = {};
15706         for(var i = 0, len = d.length; i < len; i++){
15707             v = d[i].data[dataIndex];
15708             sv = String(v);
15709             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15710                 l[sv] = true;
15711                 r[r.length] = v;
15712             }
15713         }
15714         return r;
15715     },
15716
15717     /**
15718      * Revert to a view of the Record cache with no filtering applied.
15719      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15720      */
15721     clearFilter : function(suppressEvent){
15722         if(this.snapshot && this.snapshot != this.data){
15723             this.data = this.snapshot;
15724             delete this.snapshot;
15725             if(suppressEvent !== true){
15726                 this.fireEvent("datachanged", this);
15727             }
15728         }
15729     },
15730
15731     // private
15732     afterEdit : function(record){
15733         if(this.modified.indexOf(record) == -1){
15734             this.modified.push(record);
15735         }
15736         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15737     },
15738     
15739     // private
15740     afterReject : function(record){
15741         this.modified.remove(record);
15742         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15743     },
15744
15745     // private
15746     afterCommit : function(record){
15747         this.modified.remove(record);
15748         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15749     },
15750
15751     /**
15752      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15753      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15754      */
15755     commitChanges : function(){
15756         var m = this.modified.slice(0);
15757         this.modified = [];
15758         for(var i = 0, len = m.length; i < len; i++){
15759             m[i].commit();
15760         }
15761     },
15762
15763     /**
15764      * Cancel outstanding changes on all changed records.
15765      */
15766     rejectChanges : function(){
15767         var m = this.modified.slice(0);
15768         this.modified = [];
15769         for(var i = 0, len = m.length; i < len; i++){
15770             m[i].reject();
15771         }
15772     },
15773
15774     onMetaChange : function(meta, rtype, o){
15775         this.recordType = rtype;
15776         this.fields = rtype.prototype.fields;
15777         delete this.snapshot;
15778         this.sortInfo = meta.sortInfo || this.sortInfo;
15779         this.modified = [];
15780         this.fireEvent('metachange', this, this.reader.meta);
15781     },
15782     
15783     moveIndex : function(data, type)
15784     {
15785         var index = this.indexOf(data);
15786         
15787         var newIndex = index + type;
15788         
15789         this.remove(data);
15790         
15791         this.insert(newIndex, data);
15792         
15793     }
15794 });/*
15795  * Based on:
15796  * Ext JS Library 1.1.1
15797  * Copyright(c) 2006-2007, Ext JS, LLC.
15798  *
15799  * Originally Released Under LGPL - original licence link has changed is not relivant.
15800  *
15801  * Fork - LGPL
15802  * <script type="text/javascript">
15803  */
15804
15805 /**
15806  * @class Roo.data.SimpleStore
15807  * @extends Roo.data.Store
15808  * Small helper class to make creating Stores from Array data easier.
15809  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15810  * @cfg {Array} fields An array of field definition objects, or field name strings.
15811  * @cfg {Object} an existing reader (eg. copied from another store)
15812  * @cfg {Array} data The multi-dimensional array of data
15813  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15814  * @cfg {Roo.data.Reader} reader  [not-required] 
15815  * @constructor
15816  * @param {Object} config
15817  */
15818 Roo.data.SimpleStore = function(config)
15819 {
15820     Roo.data.SimpleStore.superclass.constructor.call(this, {
15821         isLocal : true,
15822         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15823                 id: config.id
15824             },
15825             Roo.data.Record.create(config.fields)
15826         ),
15827         proxy : new Roo.data.MemoryProxy(config.data)
15828     });
15829     this.load();
15830 };
15831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843 /**
15844  * @extends Roo.data.Store
15845  * @class Roo.data.JsonStore
15846  * Small helper class to make creating Stores for JSON data easier. <br/>
15847 <pre><code>
15848 var store = new Roo.data.JsonStore({
15849     url: 'get-images.php',
15850     root: 'images',
15851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15852 });
15853 </code></pre>
15854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15855  * JsonReader and HttpProxy (unless inline data is provided).</b>
15856  * @cfg {Array} fields An array of field definition objects, or field name strings.
15857  * @constructor
15858  * @param {Object} config
15859  */
15860 Roo.data.JsonStore = function(c){
15861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15863         reader: new Roo.data.JsonReader(c, c.fields)
15864     }));
15865 };
15866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877  
15878 Roo.data.Field = function(config){
15879     if(typeof config == "string"){
15880         config = {name: config};
15881     }
15882     Roo.apply(this, config);
15883     
15884     if(!this.type){
15885         this.type = "auto";
15886     }
15887     
15888     var st = Roo.data.SortTypes;
15889     // named sortTypes are supported, here we look them up
15890     if(typeof this.sortType == "string"){
15891         this.sortType = st[this.sortType];
15892     }
15893     
15894     // set default sortType for strings and dates
15895     if(!this.sortType){
15896         switch(this.type){
15897             case "string":
15898                 this.sortType = st.asUCString;
15899                 break;
15900             case "date":
15901                 this.sortType = st.asDate;
15902                 break;
15903             default:
15904                 this.sortType = st.none;
15905         }
15906     }
15907
15908     // define once
15909     var stripRe = /[\$,%]/g;
15910
15911     // prebuilt conversion function for this field, instead of
15912     // switching every time we're reading a value
15913     if(!this.convert){
15914         var cv, dateFormat = this.dateFormat;
15915         switch(this.type){
15916             case "":
15917             case "auto":
15918             case undefined:
15919                 cv = function(v){ return v; };
15920                 break;
15921             case "string":
15922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15923                 break;
15924             case "int":
15925                 cv = function(v){
15926                     return v !== undefined && v !== null && v !== '' ?
15927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15928                     };
15929                 break;
15930             case "float":
15931                 cv = function(v){
15932                     return v !== undefined && v !== null && v !== '' ?
15933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15934                     };
15935                 break;
15936             case "bool":
15937             case "boolean":
15938                 cv = function(v){ return v === true || v === "true" || v == 1; };
15939                 break;
15940             case "date":
15941                 cv = function(v){
15942                     if(!v){
15943                         return '';
15944                     }
15945                     if(v instanceof Date){
15946                         return v;
15947                     }
15948                     if(dateFormat){
15949                         if(dateFormat == "timestamp"){
15950                             return new Date(v*1000);
15951                         }
15952                         return Date.parseDate(v, dateFormat);
15953                     }
15954                     var parsed = Date.parse(v);
15955                     return parsed ? new Date(parsed) : null;
15956                 };
15957              break;
15958             
15959         }
15960         this.convert = cv;
15961     }
15962 };
15963
15964 Roo.data.Field.prototype = {
15965     dateFormat: null,
15966     defaultValue: "",
15967     mapping: null,
15968     sortType : null,
15969     sortDir : "ASC"
15970 };/*
15971  * Based on:
15972  * Ext JS Library 1.1.1
15973  * Copyright(c) 2006-2007, Ext JS, LLC.
15974  *
15975  * Originally Released Under LGPL - original licence link has changed is not relivant.
15976  *
15977  * Fork - LGPL
15978  * <script type="text/javascript">
15979  */
15980  
15981 // Base class for reading structured data from a data source.  This class is intended to be
15982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15983
15984 /**
15985  * @class Roo.data.DataReader
15986  * @abstract
15987  * Base class for reading structured data from a data source.  This class is intended to be
15988  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15989  */
15990
15991 Roo.data.DataReader = function(meta, recordType){
15992     
15993     this.meta = meta;
15994     
15995     this.recordType = recordType instanceof Array ? 
15996         Roo.data.Record.create(recordType) : recordType;
15997 };
15998
15999 Roo.data.DataReader.prototype = {
16000     
16001     
16002     readerType : 'Data',
16003      /**
16004      * Create an empty record
16005      * @param {Object} data (optional) - overlay some values
16006      * @return {Roo.data.Record} record created.
16007      */
16008     newRow :  function(d) {
16009         var da =  {};
16010         this.recordType.prototype.fields.each(function(c) {
16011             switch( c.type) {
16012                 case 'int' : da[c.name] = 0; break;
16013                 case 'date' : da[c.name] = new Date(); break;
16014                 case 'float' : da[c.name] = 0.0; break;
16015                 case 'boolean' : da[c.name] = false; break;
16016                 default : da[c.name] = ""; break;
16017             }
16018             
16019         });
16020         return new this.recordType(Roo.apply(da, d));
16021     }
16022     
16023     
16024 };/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.data.DataProxy
16037  * @extends Roo.util.Observable
16038  * @abstract
16039  * This class is an abstract base class for implementations which provide retrieval of
16040  * unformatted data objects.<br>
16041  * <p>
16042  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16043  * (of the appropriate type which knows how to parse the data object) to provide a block of
16044  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16045  * <p>
16046  * Custom implementations must implement the load method as described in
16047  * {@link Roo.data.HttpProxy#load}.
16048  */
16049 Roo.data.DataProxy = function(){
16050     this.addEvents({
16051         /**
16052          * @event beforeload
16053          * Fires before a network request is made to retrieve a data object.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} params The params parameter to the load function.
16056          */
16057         beforeload : true,
16058         /**
16059          * @event load
16060          * Fires before the load method's callback is called.
16061          * @param {Object} This DataProxy object.
16062          * @param {Object} o The data object.
16063          * @param {Object} arg The callback argument object passed to the load function.
16064          */
16065         load : true,
16066         /**
16067          * @event loadexception
16068          * Fires if an Exception occurs during data retrieval.
16069          * @param {Object} This DataProxy object.
16070          * @param {Object} o The data object.
16071          * @param {Object} arg The callback argument object passed to the load function.
16072          * @param {Object} e The Exception.
16073          */
16074         loadexception : true
16075     });
16076     Roo.data.DataProxy.superclass.constructor.call(this);
16077 };
16078
16079 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16080
16081     /**
16082      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16083      */
16084 /*
16085  * Based on:
16086  * Ext JS Library 1.1.1
16087  * Copyright(c) 2006-2007, Ext JS, LLC.
16088  *
16089  * Originally Released Under LGPL - original licence link has changed is not relivant.
16090  *
16091  * Fork - LGPL
16092  * <script type="text/javascript">
16093  */
16094 /**
16095  * @class Roo.data.MemoryProxy
16096  * @extends Roo.data.DataProxy
16097  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16098  * to the Reader when its load method is called.
16099  * @constructor
16100  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16101  */
16102 Roo.data.MemoryProxy = function(config){
16103     var data = config;
16104     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16105         data = config.data;
16106     }
16107     Roo.data.MemoryProxy.superclass.constructor.call(this);
16108     this.data = data;
16109 };
16110
16111 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16112     
16113     /**
16114      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16115      */
16116     /**
16117      * Load data from the requested source (in this case an in-memory
16118      * data object passed to the constructor), read the data object into
16119      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16120      * process that block using the passed callback.
16121      * @param {Object} params This parameter is not used by the MemoryProxy class.
16122      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16123      * object into a block of Roo.data.Records.
16124      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16125      * The function must be passed <ul>
16126      * <li>The Record block object</li>
16127      * <li>The "arg" argument from the load function</li>
16128      * <li>A boolean success indicator</li>
16129      * </ul>
16130      * @param {Object} scope The scope in which to call the callback
16131      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16132      */
16133     load : function(params, reader, callback, scope, arg){
16134         params = params || {};
16135         var result;
16136         try {
16137             result = reader.readRecords(params.data ? params.data :this.data);
16138         }catch(e){
16139             this.fireEvent("loadexception", this, arg, null, e);
16140             callback.call(scope, null, arg, false);
16141             return;
16142         }
16143         callback.call(scope, result, arg, true);
16144     },
16145     
16146     // private
16147     update : function(params, records){
16148         
16149     }
16150 });/*
16151  * Based on:
16152  * Ext JS Library 1.1.1
16153  * Copyright(c) 2006-2007, Ext JS, LLC.
16154  *
16155  * Originally Released Under LGPL - original licence link has changed is not relivant.
16156  *
16157  * Fork - LGPL
16158  * <script type="text/javascript">
16159  */
16160 /**
16161  * @class Roo.data.HttpProxy
16162  * @extends Roo.data.DataProxy
16163  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16164  * configured to reference a certain URL.<br><br>
16165  * <p>
16166  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16167  * from which the running page was served.<br><br>
16168  * <p>
16169  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16170  * <p>
16171  * Be aware that to enable the browser to parse an XML document, the server must set
16172  * the Content-Type header in the HTTP response to "text/xml".
16173  * @constructor
16174  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16175  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16176  * will be used to make the request.
16177  */
16178 Roo.data.HttpProxy = function(conn){
16179     Roo.data.HttpProxy.superclass.constructor.call(this);
16180     // is conn a conn config or a real conn?
16181     this.conn = conn;
16182     this.useAjax = !conn || !conn.events;
16183   
16184 };
16185
16186 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16187     // thse are take from connection...
16188     
16189     /**
16190      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16191      */
16192     /**
16193      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16194      * extra parameters to each request made by this object. (defaults to undefined)
16195      */
16196     /**
16197      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16198      *  to each request made by this object. (defaults to undefined)
16199      */
16200     /**
16201      * @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)
16202      */
16203     /**
16204      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16205      */
16206      /**
16207      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16208      * @type Boolean
16209      */
16210   
16211
16212     /**
16213      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16214      * @type Boolean
16215      */
16216     /**
16217      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16218      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16219      * a finer-grained basis than the DataProxy events.
16220      */
16221     getConnection : function(){
16222         return this.useAjax ? Roo.Ajax : this.conn;
16223     },
16224
16225     /**
16226      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16227      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16228      * process that block using the passed callback.
16229      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16230      * for the request to the remote server.
16231      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16232      * object into a block of Roo.data.Records.
16233      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16234      * The function must be passed <ul>
16235      * <li>The Record block object</li>
16236      * <li>The "arg" argument from the load function</li>
16237      * <li>A boolean success indicator</li>
16238      * </ul>
16239      * @param {Object} scope The scope in which to call the callback
16240      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16241      */
16242     load : function(params, reader, callback, scope, arg){
16243         if(this.fireEvent("beforeload", this, params) !== false){
16244             var  o = {
16245                 params : params || {},
16246                 request: {
16247                     callback : callback,
16248                     scope : scope,
16249                     arg : arg
16250                 },
16251                 reader: reader,
16252                 callback : this.loadResponse,
16253                 scope: this
16254             };
16255             if(this.useAjax){
16256                 Roo.applyIf(o, this.conn);
16257                 if(this.activeRequest){
16258                     Roo.Ajax.abort(this.activeRequest);
16259                 }
16260                 this.activeRequest = Roo.Ajax.request(o);
16261             }else{
16262                 this.conn.request(o);
16263             }
16264         }else{
16265             callback.call(scope||this, null, arg, false);
16266         }
16267     },
16268
16269     // private
16270     loadResponse : function(o, success, response){
16271         delete this.activeRequest;
16272         if(!success){
16273             this.fireEvent("loadexception", this, o, response);
16274             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16275             return;
16276         }
16277         var result;
16278         try {
16279             result = o.reader.read(response);
16280         }catch(e){
16281             o.success = false;
16282             o.raw = { errorMsg : response.responseText };
16283             this.fireEvent("loadexception", this, o, response, e);
16284             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16285             return;
16286         }
16287         
16288         this.fireEvent("load", this, o, o.request.arg);
16289         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16290     },
16291
16292     // private
16293     update : function(dataSet){
16294
16295     },
16296
16297     // private
16298     updateResponse : function(dataSet){
16299
16300     }
16301 });/*
16302  * Based on:
16303  * Ext JS Library 1.1.1
16304  * Copyright(c) 2006-2007, Ext JS, LLC.
16305  *
16306  * Originally Released Under LGPL - original licence link has changed is not relivant.
16307  *
16308  * Fork - LGPL
16309  * <script type="text/javascript">
16310  */
16311
16312 /**
16313  * @class Roo.data.ScriptTagProxy
16314  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16315  * other than the originating domain of the running page.<br><br>
16316  * <p>
16317  * <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
16318  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16319  * <p>
16320  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16321  * source code that is used as the source inside a &lt;script> tag.<br><br>
16322  * <p>
16323  * In order for the browser to process the returned data, the server must wrap the data object
16324  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16325  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16326  * depending on whether the callback name was passed:
16327  * <p>
16328  * <pre><code>
16329 boolean scriptTag = false;
16330 String cb = request.getParameter("callback");
16331 if (cb != null) {
16332     scriptTag = true;
16333     response.setContentType("text/javascript");
16334 } else {
16335     response.setContentType("application/x-json");
16336 }
16337 Writer out = response.getWriter();
16338 if (scriptTag) {
16339     out.write(cb + "(");
16340 }
16341 out.print(dataBlock.toJsonString());
16342 if (scriptTag) {
16343     out.write(");");
16344 }
16345 </pre></code>
16346  *
16347  * @constructor
16348  * @param {Object} config A configuration object.
16349  */
16350 Roo.data.ScriptTagProxy = function(config){
16351     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16352     Roo.apply(this, config);
16353     this.head = document.getElementsByTagName("head")[0];
16354 };
16355
16356 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16357
16358 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16359     /**
16360      * @cfg {String} url The URL from which to request the data object.
16361      */
16362     /**
16363      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16364      */
16365     timeout : 30000,
16366     /**
16367      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16368      * the server the name of the callback function set up by the load call to process the returned data object.
16369      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16370      * javascript output which calls this named function passing the data object as its only parameter.
16371      */
16372     callbackParam : "callback",
16373     /**
16374      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16375      * name to the request.
16376      */
16377     nocache : true,
16378
16379     /**
16380      * Load data from the configured URL, read the data object into
16381      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16382      * process that block using the passed callback.
16383      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16384      * for the request to the remote server.
16385      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16386      * object into a block of Roo.data.Records.
16387      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16388      * The function must be passed <ul>
16389      * <li>The Record block object</li>
16390      * <li>The "arg" argument from the load function</li>
16391      * <li>A boolean success indicator</li>
16392      * </ul>
16393      * @param {Object} scope The scope in which to call the callback
16394      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16395      */
16396     load : function(params, reader, callback, scope, arg){
16397         if(this.fireEvent("beforeload", this, params) !== false){
16398
16399             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16400
16401             var url = this.url;
16402             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16403             if(this.nocache){
16404                 url += "&_dc=" + (new Date().getTime());
16405             }
16406             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16407             var trans = {
16408                 id : transId,
16409                 cb : "stcCallback"+transId,
16410                 scriptId : "stcScript"+transId,
16411                 params : params,
16412                 arg : arg,
16413                 url : url,
16414                 callback : callback,
16415                 scope : scope,
16416                 reader : reader
16417             };
16418             var conn = this;
16419
16420             window[trans.cb] = function(o){
16421                 conn.handleResponse(o, trans);
16422             };
16423
16424             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16425
16426             if(this.autoAbort !== false){
16427                 this.abort();
16428             }
16429
16430             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16431
16432             var script = document.createElement("script");
16433             script.setAttribute("src", url);
16434             script.setAttribute("type", "text/javascript");
16435             script.setAttribute("id", trans.scriptId);
16436             this.head.appendChild(script);
16437
16438             this.trans = trans;
16439         }else{
16440             callback.call(scope||this, null, arg, false);
16441         }
16442     },
16443
16444     // private
16445     isLoading : function(){
16446         return this.trans ? true : false;
16447     },
16448
16449     /**
16450      * Abort the current server request.
16451      */
16452     abort : function(){
16453         if(this.isLoading()){
16454             this.destroyTrans(this.trans);
16455         }
16456     },
16457
16458     // private
16459     destroyTrans : function(trans, isLoaded){
16460         this.head.removeChild(document.getElementById(trans.scriptId));
16461         clearTimeout(trans.timeoutId);
16462         if(isLoaded){
16463             window[trans.cb] = undefined;
16464             try{
16465                 delete window[trans.cb];
16466             }catch(e){}
16467         }else{
16468             // if hasn't been loaded, wait for load to remove it to prevent script error
16469             window[trans.cb] = function(){
16470                 window[trans.cb] = undefined;
16471                 try{
16472                     delete window[trans.cb];
16473                 }catch(e){}
16474             };
16475         }
16476     },
16477
16478     // private
16479     handleResponse : function(o, trans){
16480         this.trans = false;
16481         this.destroyTrans(trans, true);
16482         var result;
16483         try {
16484             result = trans.reader.readRecords(o);
16485         }catch(e){
16486             this.fireEvent("loadexception", this, o, trans.arg, e);
16487             trans.callback.call(trans.scope||window, null, trans.arg, false);
16488             return;
16489         }
16490         this.fireEvent("load", this, o, trans.arg);
16491         trans.callback.call(trans.scope||window, result, trans.arg, true);
16492     },
16493
16494     // private
16495     handleFailure : function(trans){
16496         this.trans = false;
16497         this.destroyTrans(trans, false);
16498         this.fireEvent("loadexception", this, null, trans.arg);
16499         trans.callback.call(trans.scope||window, null, trans.arg, false);
16500     }
16501 });/*
16502  * Based on:
16503  * Ext JS Library 1.1.1
16504  * Copyright(c) 2006-2007, Ext JS, LLC.
16505  *
16506  * Originally Released Under LGPL - original licence link has changed is not relivant.
16507  *
16508  * Fork - LGPL
16509  * <script type="text/javascript">
16510  */
16511
16512 /**
16513  * @class Roo.data.JsonReader
16514  * @extends Roo.data.DataReader
16515  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16516  * based on mappings in a provided Roo.data.Record constructor.
16517  * 
16518  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16519  * in the reply previously. 
16520  * 
16521  * <p>
16522  * Example code:
16523  * <pre><code>
16524 var RecordDef = Roo.data.Record.create([
16525     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16526     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16527 ]);
16528 var myReader = new Roo.data.JsonReader({
16529     totalProperty: "results",    // The property which contains the total dataset size (optional)
16530     root: "rows",                // The property which contains an Array of row objects
16531     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16532 }, RecordDef);
16533 </code></pre>
16534  * <p>
16535  * This would consume a JSON file like this:
16536  * <pre><code>
16537 { 'results': 2, 'rows': [
16538     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16539     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16540 }
16541 </code></pre>
16542  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16543  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16544  * paged from the remote server.
16545  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16546  * @cfg {String} root name of the property which contains the Array of row objects.
16547  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16548  * @cfg {Array} fields Array of field definition objects
16549  * @constructor
16550  * Create a new JsonReader
16551  * @param {Object} meta Metadata configuration options
16552  * @param {Object} recordType Either an Array of field definition objects,
16553  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16554  */
16555 Roo.data.JsonReader = function(meta, recordType){
16556     
16557     meta = meta || {};
16558     // set some defaults:
16559     Roo.applyIf(meta, {
16560         totalProperty: 'total',
16561         successProperty : 'success',
16562         root : 'data',
16563         id : 'id'
16564     });
16565     
16566     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16567 };
16568 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16569     
16570     readerType : 'Json',
16571     
16572     /**
16573      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16574      * Used by Store query builder to append _requestMeta to params.
16575      * 
16576      */
16577     metaFromRemote : false,
16578     /**
16579      * This method is only used by a DataProxy which has retrieved data from a remote server.
16580      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16581      * @return {Object} data A data block which is used by an Roo.data.Store object as
16582      * a cache of Roo.data.Records.
16583      */
16584     read : function(response){
16585         var json = response.responseText;
16586        
16587         var o = /* eval:var:o */ eval("("+json+")");
16588         if(!o) {
16589             throw {message: "JsonReader.read: Json object not found"};
16590         }
16591         
16592         if(o.metaData){
16593             
16594             delete this.ef;
16595             this.metaFromRemote = true;
16596             this.meta = o.metaData;
16597             this.recordType = Roo.data.Record.create(o.metaData.fields);
16598             this.onMetaChange(this.meta, this.recordType, o);
16599         }
16600         return this.readRecords(o);
16601     },
16602
16603     // private function a store will implement
16604     onMetaChange : function(meta, recordType, o){
16605
16606     },
16607
16608     /**
16609          * @ignore
16610          */
16611     simpleAccess: function(obj, subsc) {
16612         return obj[subsc];
16613     },
16614
16615         /**
16616          * @ignore
16617          */
16618     getJsonAccessor: function(){
16619         var re = /[\[\.]/;
16620         return function(expr) {
16621             try {
16622                 return(re.test(expr))
16623                     ? new Function("obj", "return obj." + expr)
16624                     : function(obj){
16625                         return obj[expr];
16626                     };
16627             } catch(e){}
16628             return Roo.emptyFn;
16629         };
16630     }(),
16631
16632     /**
16633      * Create a data block containing Roo.data.Records from an XML document.
16634      * @param {Object} o An object which contains an Array of row objects in the property specified
16635      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16636      * which contains the total size of the dataset.
16637      * @return {Object} data A data block which is used by an Roo.data.Store object as
16638      * a cache of Roo.data.Records.
16639      */
16640     readRecords : function(o){
16641         /**
16642          * After any data loads, the raw JSON data is available for further custom processing.
16643          * @type Object
16644          */
16645         this.o = o;
16646         var s = this.meta, Record = this.recordType,
16647             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16648
16649 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16650         if (!this.ef) {
16651             if(s.totalProperty) {
16652                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16653                 }
16654                 if(s.successProperty) {
16655                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16656                 }
16657                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16658                 if (s.id) {
16659                         var g = this.getJsonAccessor(s.id);
16660                         this.getId = function(rec) {
16661                                 var r = g(rec);  
16662                                 return (r === undefined || r === "") ? null : r;
16663                         };
16664                 } else {
16665                         this.getId = function(){return null;};
16666                 }
16667             this.ef = [];
16668             for(var jj = 0; jj < fl; jj++){
16669                 f = fi[jj];
16670                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16671                 this.ef[jj] = this.getJsonAccessor(map);
16672             }
16673         }
16674
16675         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16676         if(s.totalProperty){
16677             var vt = parseInt(this.getTotal(o), 10);
16678             if(!isNaN(vt)){
16679                 totalRecords = vt;
16680             }
16681         }
16682         if(s.successProperty){
16683             var vs = this.getSuccess(o);
16684             if(vs === false || vs === 'false'){
16685                 success = false;
16686             }
16687         }
16688         var records = [];
16689         for(var i = 0; i < c; i++){
16690             var n = root[i];
16691             var values = {};
16692             var id = this.getId(n);
16693             for(var j = 0; j < fl; j++){
16694                 f = fi[j];
16695                                 var v = this.ef[j](n);
16696                                 if (!f.convert) {
16697                                         Roo.log('missing convert for ' + f.name);
16698                                         Roo.log(f);
16699                                         continue;
16700                                 }
16701                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16702             }
16703                         if (!Record) {
16704                                 return {
16705                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16706                                         success : false,
16707                                         records : [],
16708                                         totalRecords : 0
16709                                 };
16710                         }
16711             var record = new Record(values, id);
16712             record.json = n;
16713             records[i] = record;
16714         }
16715         return {
16716             raw : o,
16717             success : success,
16718             records : records,
16719             totalRecords : totalRecords
16720         };
16721     },
16722     // used when loading children.. @see loadDataFromChildren
16723     toLoadData: function(rec)
16724     {
16725         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16726         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16727         return { data : data, total : data.length };
16728         
16729     }
16730 });/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.data.ArrayReader
16743  * @extends Roo.data.DataReader
16744  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16745  * Each element of that Array represents a row of data fields. The
16746  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16747  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16748  * <p>
16749  * Example code:.
16750  * <pre><code>
16751 var RecordDef = Roo.data.Record.create([
16752     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16753     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16754 ]);
16755 var myReader = new Roo.data.ArrayReader({
16756     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16757 }, RecordDef);
16758 </code></pre>
16759  * <p>
16760  * This would consume an Array like this:
16761  * <pre><code>
16762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16763   </code></pre>
16764  
16765  * @constructor
16766  * Create a new JsonReader
16767  * @param {Object} meta Metadata configuration options.
16768  * @param {Object|Array} recordType Either an Array of field definition objects
16769  * 
16770  * @cfg {Array} fields Array of field definition objects
16771  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16772  * as specified to {@link Roo.data.Record#create},
16773  * or an {@link Roo.data.Record} object
16774  *
16775  * 
16776  * created using {@link Roo.data.Record#create}.
16777  */
16778 Roo.data.ArrayReader = function(meta, recordType)
16779 {    
16780     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16781 };
16782
16783 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16784     
16785       /**
16786      * Create a data block containing Roo.data.Records from an XML document.
16787      * @param {Object} o An Array of row objects which represents the dataset.
16788      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16789      * a cache of Roo.data.Records.
16790      */
16791     readRecords : function(o)
16792     {
16793         var sid = this.meta ? this.meta.id : null;
16794         var recordType = this.recordType, fields = recordType.prototype.fields;
16795         var records = [];
16796         var root = o;
16797         for(var i = 0; i < root.length; i++){
16798             var n = root[i];
16799             var values = {};
16800             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16801             for(var j = 0, jlen = fields.length; j < jlen; j++){
16802                 var f = fields.items[j];
16803                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16804                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16805                 v = f.convert(v);
16806                 values[f.name] = v;
16807             }
16808             var record = new recordType(values, id);
16809             record.json = n;
16810             records[records.length] = record;
16811         }
16812         return {
16813             records : records,
16814             totalRecords : records.length
16815         };
16816     },
16817     // used when loading children.. @see loadDataFromChildren
16818     toLoadData: function(rec)
16819     {
16820         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16821         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16822         
16823     }
16824     
16825     
16826 });/*
16827  * - LGPL
16828  * * 
16829  */
16830
16831 /**
16832  * @class Roo.bootstrap.form.ComboBox
16833  * @extends Roo.bootstrap.form.TriggerField
16834  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16835  * @cfg {Boolean} append (true|false) default false
16836  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16837  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16838  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16839  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16840  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16841  * @cfg {Boolean} animate default true
16842  * @cfg {Boolean} emptyResultText only for touch device
16843  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16844  * @cfg {String} emptyTitle default ''
16845  * @cfg {Number} width fixed with? experimental
16846  * @constructor
16847  * Create a new ComboBox.
16848  * @param {Object} config Configuration options
16849  */
16850 Roo.bootstrap.form.ComboBox = function(config){
16851     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854          * @event expand
16855          * Fires when the dropdown list is expanded
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'expand' : true,
16859         /**
16860          * @event collapse
16861          * Fires when the dropdown list is collapsed
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'collapse' : true,
16865         /**
16866          * @event beforeselect
16867          * Fires before a list item is selected. Return false to cancel the selection.
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         * @param {Roo.data.Record} record The data record returned from the underlying store
16870         * @param {Number} index The index of the selected item in the dropdown list
16871         */
16872         'beforeselect' : true,
16873         /**
16874          * @event select
16875          * Fires when a list item is selected
16876         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16878         * @param {Number} index The index of the selected item in the dropdown list
16879         */
16880         'select' : true,
16881         /**
16882          * @event beforequery
16883          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16884          * The event object passed has these properties:
16885         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886         * @param {String} query The query
16887         * @param {Boolean} forceAll true to force "all" query
16888         * @param {Boolean} cancel true to cancel the query
16889         * @param {Object} e The query event object
16890         */
16891         'beforequery': true,
16892          /**
16893          * @event add
16894          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'add' : true,
16898         /**
16899          * @event edit
16900          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16903         */
16904         'edit' : true,
16905         /**
16906          * @event remove
16907          * Fires when the remove value from the combobox array
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         */
16910         'remove' : true,
16911         /**
16912          * @event afterremove
16913          * Fires when the remove value from the combobox array
16914         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915         */
16916         'afterremove' : true,
16917         /**
16918          * @event specialfilter
16919          * Fires when specialfilter
16920             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921             */
16922         'specialfilter' : true,
16923         /**
16924          * @event tick
16925          * Fires when tick the element
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             */
16928         'tick' : true,
16929         /**
16930          * @event touchviewdisplay
16931          * Fires when touch view require special display (default is using displayField)
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             * @param {Object} cfg set html .
16934             */
16935         'touchviewdisplay' : true
16936         
16937     });
16938     
16939     this.item = [];
16940     this.tickItems = [];
16941     
16942     this.selectedIndex = -1;
16943     if(this.mode == 'local'){
16944         if(config.queryDelay === undefined){
16945             this.queryDelay = 10;
16946         }
16947         if(config.minChars === undefined){
16948             this.minChars = 0;
16949         }
16950     }
16951 };
16952
16953 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16954      
16955     /**
16956      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16957      * rendering into an Roo.Editor, defaults to false)
16958      */
16959     /**
16960      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16961      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16962      */
16963     /**
16964      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16965      */
16966     /**
16967      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16968      * the dropdown list (defaults to undefined, with no header element)
16969      */
16970
16971      /**
16972      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16973      */
16974      
16975      /**
16976      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16977      */
16978     listWidth: undefined,
16979     /**
16980      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16981      * mode = 'remote' or 'text' if mode = 'local')
16982      */
16983     displayField: undefined,
16984     
16985     /**
16986      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16987      * mode = 'remote' or 'value' if mode = 'local'). 
16988      * Note: use of a valueField requires the user make a selection
16989      * in order for a value to be mapped.
16990      */
16991     valueField: undefined,
16992     /**
16993      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16994      */
16995     modalTitle : '',
16996     
16997     /**
16998      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16999      * field's data value (defaults to the underlying DOM element's name)
17000      */
17001     hiddenName: undefined,
17002     /**
17003      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17004      */
17005     listClass: '',
17006     /**
17007      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17008      */
17009     selectedClass: 'active',
17010     
17011     /**
17012      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17013      */
17014     shadow:'sides',
17015     /**
17016      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17017      * anchor positions (defaults to 'tl-bl')
17018      */
17019     listAlign: 'tl-bl?',
17020     /**
17021      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17022      */
17023     maxHeight: 300,
17024     /**
17025      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17026      * query specified by the allQuery config option (defaults to 'query')
17027      */
17028     triggerAction: 'query',
17029     /**
17030      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17031      * (defaults to 4, does not apply if editable = false)
17032      */
17033     minChars : 4,
17034     /**
17035      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17036      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17037      */
17038     typeAhead: false,
17039     /**
17040      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17041      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17042      */
17043     queryDelay: 500,
17044     /**
17045      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17046      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17047      */
17048     pageSize: 0,
17049     /**
17050      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17051      * when editable = true (defaults to false)
17052      */
17053     selectOnFocus:false,
17054     /**
17055      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17056      */
17057     queryParam: 'query',
17058     /**
17059      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17060      * when mode = 'remote' (defaults to 'Loading...')
17061      */
17062     loadingText: 'Loading...',
17063     /**
17064      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17065      */
17066     resizable: false,
17067     /**
17068      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17069      */
17070     handleHeight : 8,
17071     /**
17072      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17073      * traditional select (defaults to true)
17074      */
17075     editable: true,
17076     /**
17077      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17078      */
17079     allQuery: '',
17080     /**
17081      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17082      */
17083     mode: 'remote',
17084     /**
17085      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17086      * listWidth has a higher value)
17087      */
17088     minListWidth : 70,
17089     /**
17090      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17091      * allow the user to set arbitrary text into the field (defaults to false)
17092      */
17093     forceSelection:false,
17094     /**
17095      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17096      * if typeAhead = true (defaults to 250)
17097      */
17098     typeAheadDelay : 250,
17099     /**
17100      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17101      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17102      */
17103     valueNotFoundText : undefined,
17104     /**
17105      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17106      */
17107     blockFocus : false,
17108     
17109     /**
17110      * @cfg {Boolean} disableClear Disable showing of clear button.
17111      */
17112     disableClear : false,
17113     /**
17114      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17115      */
17116     alwaysQuery : false,
17117     
17118     /**
17119      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17120      */
17121     multiple : false,
17122     
17123     /**
17124      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     invalidClass : "has-warning",
17127     
17128     /**
17129      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17130      */
17131     validClass : "has-success",
17132     
17133     /**
17134      * @cfg {Boolean} specialFilter (true|false) special filter default false
17135      */
17136     specialFilter : false,
17137     
17138     /**
17139      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17140      */
17141     mobileTouchView : true,
17142     
17143     /**
17144      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17145      */
17146     useNativeIOS : false,
17147     
17148     /**
17149      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17150      */
17151     mobile_restrict_height : false,
17152     
17153     ios_options : false,
17154     
17155     //private
17156     addicon : false,
17157     editicon: false,
17158     
17159     page: 0,
17160     hasQuery: false,
17161     append: false,
17162     loadNext: false,
17163     autoFocus : true,
17164     tickable : false,
17165     btnPosition : 'right',
17166     triggerList : true,
17167     showToggleBtn : true,
17168     animate : true,
17169     emptyResultText: 'Empty',
17170     triggerText : 'Select',
17171     emptyTitle : '',
17172     width : false,
17173     
17174     // element that contains real text value.. (when hidden is used..)
17175     
17176     getAutoCreate : function()
17177     {   
17178         var cfg = false;
17179         //render
17180         /*
17181          * Render classic select for iso
17182          */
17183         
17184         if(Roo.isIOS && this.useNativeIOS){
17185             cfg = this.getAutoCreateNativeIOS();
17186             return cfg;
17187         }
17188         
17189         /*
17190          * Touch Devices
17191          */
17192         
17193         if(Roo.isTouch && this.mobileTouchView){
17194             cfg = this.getAutoCreateTouchView();
17195             return cfg;;
17196         }
17197         
17198         /*
17199          *  Normal ComboBox
17200          */
17201         if(!this.tickable){
17202             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17203             return cfg;
17204         }
17205         
17206         /*
17207          *  ComboBox with tickable selections
17208          */
17209              
17210         var align = this.labelAlign || this.parentLabelAlign();
17211         
17212         cfg = {
17213             cls : 'form-group roo-combobox-tickable' //input-group
17214         };
17215         
17216         var btn_text_select = '';
17217         var btn_text_done = '';
17218         var btn_text_cancel = '';
17219         
17220         if (this.btn_text_show) {
17221             btn_text_select = 'Select';
17222             btn_text_done = 'Done';
17223             btn_text_cancel = 'Cancel'; 
17224         }
17225         
17226         var buttons = {
17227             tag : 'div',
17228             cls : 'tickable-buttons',
17229             cn : [
17230                 {
17231                     tag : 'button',
17232                     type : 'button',
17233                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17234                     //html : this.triggerText
17235                     html: btn_text_select
17236                 },
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     name : 'ok',
17241                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17242                     //html : 'Done'
17243                     html: btn_text_done
17244                 },
17245                 {
17246                     tag : 'button',
17247                     type : 'button',
17248                     name : 'cancel',
17249                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17250                     //html : 'Cancel'
17251                     html: btn_text_cancel
17252                 }
17253             ]
17254         };
17255         
17256         if(this.editable){
17257             buttons.cn.unshift({
17258                 tag: 'input',
17259                 cls: 'roo-select2-search-field-input'
17260             });
17261         }
17262         
17263         var _this = this;
17264         
17265         Roo.each(buttons.cn, function(c){
17266             if (_this.size) {
17267                 c.cls += ' btn-' + _this.size;
17268             }
17269
17270             if (_this.disabled) {
17271                 c.disabled = true;
17272             }
17273         });
17274         
17275         var box = {
17276             tag: 'div',
17277             style : 'display: contents',
17278             cn: [
17279                 {
17280                     tag: 'input',
17281                     type : 'hidden',
17282                     cls: 'form-hidden-field'
17283                 },
17284                 {
17285                     tag: 'ul',
17286                     cls: 'roo-select2-choices',
17287                     cn:[
17288                         {
17289                             tag: 'li',
17290                             cls: 'roo-select2-search-field',
17291                             cn: [
17292                                 buttons
17293                             ]
17294                         }
17295                     ]
17296                 }
17297             ]
17298         };
17299         
17300         var combobox = {
17301             cls: 'roo-select2-container input-group roo-select2-container-multi',
17302             cn: [
17303                 
17304                 box
17305 //                {
17306 //                    tag: 'ul',
17307 //                    cls: 'typeahead typeahead-long dropdown-menu',
17308 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17309 //                }
17310             ]
17311         };
17312         
17313         if(this.hasFeedback && !this.allowBlank){
17314             
17315             var feedback = {
17316                 tag: 'span',
17317                 cls: 'glyphicon form-control-feedback'
17318             };
17319
17320             combobox.cn.push(feedback);
17321         }
17322         
17323         
17324         
17325         var indicator = {
17326             tag : 'i',
17327             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17328             tooltip : 'This field is required'
17329         };
17330         if (Roo.bootstrap.version == 4) {
17331             indicator = {
17332                 tag : 'i',
17333                 style : 'display:none'
17334             };
17335         }
17336         if (align ==='left' && this.fieldLabel.length) {
17337             
17338             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17339             
17340             cfg.cn = [
17341                 indicator,
17342                 {
17343                     tag: 'label',
17344                     'for' :  id,
17345                     cls : 'control-label col-form-label',
17346                     html : this.fieldLabel
17347
17348                 },
17349                 {
17350                     cls : "", 
17351                     cn: [
17352                         combobox
17353                     ]
17354                 }
17355
17356             ];
17357             
17358             var labelCfg = cfg.cn[1];
17359             var contentCfg = cfg.cn[2];
17360             
17361
17362             if(this.indicatorpos == 'right'){
17363                 
17364                 cfg.cn = [
17365                     {
17366                         tag: 'label',
17367                         'for' :  id,
17368                         cls : 'control-label col-form-label',
17369                         cn : [
17370                             {
17371                                 tag : 'span',
17372                                 html : this.fieldLabel
17373                             },
17374                             indicator
17375                         ]
17376                     },
17377                     {
17378                         cls : "",
17379                         cn: [
17380                             combobox
17381                         ]
17382                     }
17383
17384                 ];
17385                 
17386                 
17387                 
17388                 labelCfg = cfg.cn[0];
17389                 contentCfg = cfg.cn[1];
17390             
17391             }
17392             
17393             if(this.labelWidth > 12){
17394                 labelCfg.style = "width: " + this.labelWidth + 'px';
17395             }
17396             if(this.width * 1 > 0){
17397                 contentCfg.style = "width: " + this.width + 'px';
17398             }
17399             if(this.labelWidth < 13 && this.labelmd == 0){
17400                 this.labelmd = this.labelWidth;
17401             }
17402             
17403             if(this.labellg > 0){
17404                 labelCfg.cls += ' col-lg-' + this.labellg;
17405                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17406             }
17407             
17408             if(this.labelmd > 0){
17409                 labelCfg.cls += ' col-md-' + this.labelmd;
17410                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17411             }
17412             
17413             if(this.labelsm > 0){
17414                 labelCfg.cls += ' col-sm-' + this.labelsm;
17415                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17416             }
17417             
17418             if(this.labelxs > 0){
17419                 labelCfg.cls += ' col-xs-' + this.labelxs;
17420                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17421             }
17422                 
17423                 
17424         } else if ( this.fieldLabel.length) {
17425 //                Roo.log(" label");
17426                  cfg.cn = [
17427                    indicator,
17428                     {
17429                         tag: 'label',
17430                         //cls : 'input-group-addon',
17431                         html : this.fieldLabel
17432                     },
17433                     combobox
17434                 ];
17435                 
17436                 if(this.indicatorpos == 'right'){
17437                     cfg.cn = [
17438                         {
17439                             tag: 'label',
17440                             //cls : 'input-group-addon',
17441                             html : this.fieldLabel
17442                         },
17443                         indicator,
17444                         combobox
17445                     ];
17446                     
17447                 }
17448
17449         } else {
17450             
17451 //                Roo.log(" no label && no align");
17452                 cfg = combobox
17453                      
17454                 
17455         }
17456          
17457         var settings=this;
17458         ['xs','sm','md','lg'].map(function(size){
17459             if (settings[size]) {
17460                 cfg.cls += ' col-' + size + '-' + settings[size];
17461             }
17462         });
17463         
17464         return cfg;
17465         
17466     },
17467     
17468     _initEventsCalled : false,
17469     
17470     // private
17471     initEvents: function()
17472     {   
17473         if (this._initEventsCalled) { // as we call render... prevent looping...
17474             return;
17475         }
17476         this._initEventsCalled = true;
17477         
17478         if (!this.store) {
17479             throw "can not find store for combo";
17480         }
17481         
17482         this.indicator = this.indicatorEl();
17483         
17484         this.store = Roo.factory(this.store, Roo.data);
17485         this.store.parent = this;
17486         
17487         // if we are building from html. then this element is so complex, that we can not really
17488         // use the rendered HTML.
17489         // so we have to trash and replace the previous code.
17490         if (Roo.XComponent.build_from_html) {
17491             // remove this element....
17492             var e = this.el.dom, k=0;
17493             while (e ) { e = e.previousSibling;  ++k;}
17494
17495             this.el.remove();
17496             
17497             this.el=false;
17498             this.rendered = false;
17499             
17500             this.render(this.parent().getChildContainer(true), k);
17501         }
17502         
17503         if(Roo.isIOS && this.useNativeIOS){
17504             this.initIOSView();
17505             return;
17506         }
17507         
17508         /*
17509          * Touch Devices
17510          */
17511         
17512         if(Roo.isTouch && this.mobileTouchView){
17513             this.initTouchView();
17514             return;
17515         }
17516         
17517         if(this.tickable){
17518             this.initTickableEvents();
17519             return;
17520         }
17521         
17522         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17523         
17524         if(this.hiddenName){
17525             
17526             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17527             
17528             this.hiddenField.dom.value =
17529                 this.hiddenValue !== undefined ? this.hiddenValue :
17530                 this.value !== undefined ? this.value : '';
17531
17532             // prevent input submission
17533             this.el.dom.removeAttribute('name');
17534             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17535              
17536              
17537         }
17538         //if(Roo.isGecko){
17539         //    this.el.dom.setAttribute('autocomplete', 'off');
17540         //}
17541         
17542         var cls = 'x-combo-list';
17543         
17544         //this.list = new Roo.Layer({
17545         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17546         //});
17547         
17548         var _this = this;
17549         
17550         (function(){
17551             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17552             _this.list.setWidth(lw);
17553         }).defer(100);
17554         
17555         this.list.on('mouseover', this.onViewOver, this);
17556         this.list.on('mousemove', this.onViewMove, this);
17557         this.list.on('scroll', this.onViewScroll, this);
17558         
17559         /*
17560         this.list.swallowEvent('mousewheel');
17561         this.assetHeight = 0;
17562
17563         if(this.title){
17564             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17565             this.assetHeight += this.header.getHeight();
17566         }
17567
17568         this.innerList = this.list.createChild({cls:cls+'-inner'});
17569         this.innerList.on('mouseover', this.onViewOver, this);
17570         this.innerList.on('mousemove', this.onViewMove, this);
17571         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17572         
17573         if(this.allowBlank && !this.pageSize && !this.disableClear){
17574             this.footer = this.list.createChild({cls:cls+'-ft'});
17575             this.pageTb = new Roo.Toolbar(this.footer);
17576            
17577         }
17578         if(this.pageSize){
17579             this.footer = this.list.createChild({cls:cls+'-ft'});
17580             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17581                     {pageSize: this.pageSize});
17582             
17583         }
17584         
17585         if (this.pageTb && this.allowBlank && !this.disableClear) {
17586             var _this = this;
17587             this.pageTb.add(new Roo.Toolbar.Fill(), {
17588                 cls: 'x-btn-icon x-btn-clear',
17589                 text: '&#160;',
17590                 handler: function()
17591                 {
17592                     _this.collapse();
17593                     _this.clearValue();
17594                     _this.onSelect(false, -1);
17595                 }
17596             });
17597         }
17598         if (this.footer) {
17599             this.assetHeight += this.footer.getHeight();
17600         }
17601         */
17602             
17603         if(!this.tpl){
17604             this.tpl = Roo.bootstrap.version == 4 ?
17605                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17606                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17607         }
17608
17609         this.view = new Roo.View(this.list, this.tpl, {
17610             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17611         });
17612         //this.view.wrapEl.setDisplayed(false);
17613         this.view.on('click', this.onViewClick, this);
17614         
17615         
17616         this.store.on('beforeload', this.onBeforeLoad, this);
17617         this.store.on('load', this.onLoad, this);
17618         this.store.on('loadexception', this.onLoadException, this);
17619         /*
17620         if(this.resizable){
17621             this.resizer = new Roo.Resizable(this.list,  {
17622                pinned:true, handles:'se'
17623             });
17624             this.resizer.on('resize', function(r, w, h){
17625                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17626                 this.listWidth = w;
17627                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17628                 this.restrictHeight();
17629             }, this);
17630             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17631         }
17632         */
17633         if(!this.editable){
17634             this.editable = true;
17635             this.setEditable(false);
17636         }
17637         
17638         /*
17639         
17640         if (typeof(this.events.add.listeners) != 'undefined') {
17641             
17642             this.addicon = this.wrap.createChild(
17643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17644        
17645             this.addicon.on('click', function(e) {
17646                 this.fireEvent('add', this);
17647             }, this);
17648         }
17649         if (typeof(this.events.edit.listeners) != 'undefined') {
17650             
17651             this.editicon = this.wrap.createChild(
17652                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17653             if (this.addicon) {
17654                 this.editicon.setStyle('margin-left', '40px');
17655             }
17656             this.editicon.on('click', function(e) {
17657                 
17658                 // we fire even  if inothing is selected..
17659                 this.fireEvent('edit', this, this.lastData );
17660                 
17661             }, this);
17662         }
17663         */
17664         
17665         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17666             "up" : function(e){
17667                 this.inKeyMode = true;
17668                 this.selectPrev();
17669             },
17670
17671             "down" : function(e){
17672                 if(!this.isExpanded()){
17673                     this.onTriggerClick();
17674                 }else{
17675                     this.inKeyMode = true;
17676                     this.selectNext();
17677                 }
17678             },
17679
17680             "enter" : function(e){
17681 //                this.onViewClick();
17682                 //return true;
17683                 this.collapse();
17684                 
17685                 if(this.fireEvent("specialkey", this, e)){
17686                     this.onViewClick(false);
17687                 }
17688                 
17689                 return true;
17690             },
17691
17692             "esc" : function(e){
17693                 this.collapse();
17694             },
17695
17696             "tab" : function(e){
17697                 this.collapse();
17698                 
17699                 if(this.fireEvent("specialkey", this, e)){
17700                     this.onViewClick(false);
17701                 }
17702                 
17703                 return true;
17704             },
17705
17706             scope : this,
17707
17708             doRelay : function(foo, bar, hname){
17709                 if(hname == 'down' || this.scope.isExpanded()){
17710                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17711                 }
17712                 return true;
17713             },
17714
17715             forceKeyDown: true
17716         });
17717         
17718         
17719         this.queryDelay = Math.max(this.queryDelay || 10,
17720                 this.mode == 'local' ? 10 : 250);
17721         
17722         
17723         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17724         
17725         if(this.typeAhead){
17726             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17727         }
17728         if(this.editable !== false){
17729             this.inputEl().on("keyup", this.onKeyUp, this);
17730         }
17731         if(this.forceSelection){
17732             this.inputEl().on('blur', this.doForce, this);
17733         }
17734         
17735         if(this.multiple){
17736             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17737             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17738         }
17739     },
17740     
17741     initTickableEvents: function()
17742     {   
17743         this.createList();
17744         
17745         if(this.hiddenName){
17746             
17747             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748             
17749             this.hiddenField.dom.value =
17750                 this.hiddenValue !== undefined ? this.hiddenValue :
17751                 this.value !== undefined ? this.value : '';
17752
17753             // prevent input submission
17754             this.el.dom.removeAttribute('name');
17755             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17756              
17757              
17758         }
17759         
17760 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17761         
17762         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17763         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17764         if(this.triggerList){
17765             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17766         }
17767          
17768         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17769         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17770         
17771         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17772         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17773         
17774         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17775         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17776         
17777         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17778         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780         
17781         this.okBtn.hide();
17782         this.cancelBtn.hide();
17783         
17784         var _this = this;
17785         
17786         (function(){
17787             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17788             _this.list.setWidth(lw);
17789         }).defer(100);
17790         
17791         this.list.on('mouseover', this.onViewOver, this);
17792         this.list.on('mousemove', this.onViewMove, this);
17793         
17794         this.list.on('scroll', this.onViewScroll, this);
17795         
17796         if(!this.tpl){
17797             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17798                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17799         }
17800
17801         this.view = new Roo.View(this.list, this.tpl, {
17802             singleSelect:true,
17803             tickable:true,
17804             parent:this,
17805             store: this.store,
17806             selectedClass: this.selectedClass
17807         });
17808         
17809         //this.view.wrapEl.setDisplayed(false);
17810         this.view.on('click', this.onViewClick, this);
17811         
17812         
17813         
17814         this.store.on('beforeload', this.onBeforeLoad, this);
17815         this.store.on('load', this.onLoad, this);
17816         this.store.on('loadexception', this.onLoadException, this);
17817         
17818         if(this.editable){
17819             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17820                 "up" : function(e){
17821                     this.inKeyMode = true;
17822                     this.selectPrev();
17823                 },
17824
17825                 "down" : function(e){
17826                     this.inKeyMode = true;
17827                     this.selectNext();
17828                 },
17829
17830                 "enter" : function(e){
17831                     if(this.fireEvent("specialkey", this, e)){
17832                         this.onViewClick(false);
17833                     }
17834                     
17835                     return true;
17836                 },
17837
17838                 "esc" : function(e){
17839                     this.onTickableFooterButtonClick(e, false, false);
17840                 },
17841
17842                 "tab" : function(e){
17843                     this.fireEvent("specialkey", this, e);
17844                     
17845                     this.onTickableFooterButtonClick(e, false, false);
17846                     
17847                     return true;
17848                 },
17849
17850                 scope : this,
17851
17852                 doRelay : function(e, fn, key){
17853                     if(this.scope.isExpanded()){
17854                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17855                     }
17856                     return true;
17857                 },
17858
17859                 forceKeyDown: true
17860             });
17861         }
17862         
17863         this.queryDelay = Math.max(this.queryDelay || 10,
17864                 this.mode == 'local' ? 10 : 250);
17865         
17866         
17867         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17868         
17869         if(this.typeAhead){
17870             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17871         }
17872         
17873         if(this.editable !== false){
17874             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17875         }
17876         
17877         this.indicator = this.indicatorEl();
17878         
17879         if(this.indicator){
17880             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17881             this.indicator.hide();
17882         }
17883         
17884     },
17885
17886     onDestroy : function(){
17887         if(this.view){
17888             this.view.setStore(null);
17889             this.view.el.removeAllListeners();
17890             this.view.el.remove();
17891             this.view.purgeListeners();
17892         }
17893         if(this.list){
17894             this.list.dom.innerHTML  = '';
17895         }
17896         
17897         if(this.store){
17898             this.store.un('beforeload', this.onBeforeLoad, this);
17899             this.store.un('load', this.onLoad, this);
17900             this.store.un('loadexception', this.onLoadException, this);
17901         }
17902         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17903     },
17904
17905     // private
17906     fireKey : function(e){
17907         if(e.isNavKeyPress() && !this.list.isVisible()){
17908             this.fireEvent("specialkey", this, e);
17909         }
17910     },
17911
17912     // private
17913     onResize: function(w, h)
17914     {
17915         
17916         
17917 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17918 //        
17919 //        if(typeof w != 'number'){
17920 //            // we do not handle it!?!?
17921 //            return;
17922 //        }
17923 //        var tw = this.trigger.getWidth();
17924 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17925 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17926 //        var x = w - tw;
17927 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17928 //            
17929 //        //this.trigger.setStyle('left', x+'px');
17930 //        
17931 //        if(this.list && this.listWidth === undefined){
17932 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17933 //            this.list.setWidth(lw);
17934 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17935 //        }
17936         
17937     
17938         
17939     },
17940
17941     /**
17942      * Allow or prevent the user from directly editing the field text.  If false is passed,
17943      * the user will only be able to select from the items defined in the dropdown list.  This method
17944      * is the runtime equivalent of setting the 'editable' config option at config time.
17945      * @param {Boolean} value True to allow the user to directly edit the field text
17946      */
17947     setEditable : function(value){
17948         if(value == this.editable){
17949             return;
17950         }
17951         this.editable = value;
17952         if(!value){
17953             this.inputEl().dom.setAttribute('readOnly', true);
17954             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17955             this.inputEl().addClass('x-combo-noedit');
17956         }else{
17957             this.inputEl().dom.removeAttribute('readOnly');
17958             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17959             this.inputEl().removeClass('x-combo-noedit');
17960         }
17961     },
17962
17963     // private
17964     
17965     onBeforeLoad : function(combo,opts){
17966         if(!this.hasFocus){
17967             return;
17968         }
17969          if (!opts.add) {
17970             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17971          }
17972         this.restrictHeight();
17973         this.selectedIndex = -1;
17974     },
17975
17976     // private
17977     onLoad : function(){
17978         
17979         this.hasQuery = false;
17980         
17981         if(!this.hasFocus){
17982             return;
17983         }
17984         
17985         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17986             this.loading.hide();
17987         }
17988         
17989         if(this.store.getCount() > 0){
17990             
17991             this.expand();
17992             this.restrictHeight();
17993             if(this.lastQuery == this.allQuery){
17994                 if(this.editable && !this.tickable){
17995                     this.inputEl().dom.select();
17996                 }
17997                 
17998                 if(
17999                     !this.selectByValue(this.value, true) &&
18000                     this.autoFocus && 
18001                     (
18002                         !this.store.lastOptions ||
18003                         typeof(this.store.lastOptions.add) == 'undefined' || 
18004                         this.store.lastOptions.add != true
18005                     )
18006                 ){
18007                     this.select(0, true);
18008                 }
18009             }else{
18010                 if(this.autoFocus){
18011                     this.selectNext();
18012                 }
18013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18014                     this.taTask.delay(this.typeAheadDelay);
18015                 }
18016             }
18017         }else{
18018             this.onEmptyResults();
18019         }
18020         
18021         //this.el.focus();
18022     },
18023     // private
18024     onLoadException : function()
18025     {
18026         this.hasQuery = false;
18027         
18028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18029             this.loading.hide();
18030         }
18031         
18032         if(this.tickable && this.editable){
18033             return;
18034         }
18035         
18036         this.collapse();
18037         // only causes errors at present
18038         //Roo.log(this.store.reader.jsonData);
18039         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18040             // fixme
18041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18042         //}
18043         
18044         
18045     },
18046     // private
18047     onTypeAhead : function(){
18048         if(this.store.getCount() > 0){
18049             var r = this.store.getAt(0);
18050             var newValue = r.data[this.displayField];
18051             var len = newValue.length;
18052             var selStart = this.getRawValue().length;
18053             
18054             if(selStart != len){
18055                 this.setRawValue(newValue);
18056                 this.selectText(selStart, newValue.length);
18057             }
18058         }
18059     },
18060
18061     // private
18062     onSelect : function(record, index){
18063         
18064         if(this.fireEvent('beforeselect', this, record, index) !== false){
18065         
18066             this.setFromData(index > -1 ? record.data : false);
18067             
18068             this.collapse();
18069             this.fireEvent('select', this, record, index);
18070         }
18071     },
18072
18073     /**
18074      * Returns the currently selected field value or empty string if no value is set.
18075      * @return {String} value The selected value
18076      */
18077     getValue : function()
18078     {
18079         if(Roo.isIOS && this.useNativeIOS){
18080             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18081         }
18082         
18083         if(this.multiple){
18084             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18085         }
18086         
18087         if(this.valueField){
18088             return typeof this.value != 'undefined' ? this.value : '';
18089         }else{
18090             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18091         }
18092     },
18093     
18094     getRawValue : function()
18095     {
18096         if(Roo.isIOS && this.useNativeIOS){
18097             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18098         }
18099         
18100         var v = this.inputEl().getValue();
18101         
18102         return v;
18103     },
18104
18105     /**
18106      * Clears any text/value currently set in the field
18107      */
18108     clearValue : function(){
18109         
18110         if(this.hiddenField){
18111             this.hiddenField.dom.value = '';
18112         }
18113         this.value = '';
18114         this.setRawValue('');
18115         this.lastSelectionText = '';
18116         this.lastData = false;
18117         
18118         var close = this.closeTriggerEl();
18119         
18120         if(close){
18121             close.hide();
18122         }
18123         
18124         this.validate();
18125         
18126     },
18127
18128     /**
18129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18130      * will be displayed in the field.  If the value does not match the data value of an existing item,
18131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18132      * Otherwise the field will be blank (although the value will still be set).
18133      * @param {String} value The value to match
18134      */
18135     setValue : function(v)
18136     {
18137         if(Roo.isIOS && this.useNativeIOS){
18138             this.setIOSValue(v);
18139             return;
18140         }
18141         
18142         if(this.multiple){
18143             this.syncValue();
18144             return;
18145         }
18146         
18147         var text = v;
18148         if(this.valueField){
18149             var r = this.findRecord(this.valueField, v);
18150             if(r){
18151                 text = r.data[this.displayField];
18152             }else if(this.valueNotFoundText !== undefined){
18153                 text = this.valueNotFoundText;
18154             }
18155         }
18156         this.lastSelectionText = text;
18157         if(this.hiddenField){
18158             this.hiddenField.dom.value = v;
18159         }
18160         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18161         this.value = v;
18162         
18163         var close = this.closeTriggerEl();
18164         
18165         if(close){
18166             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18167         }
18168         
18169         this.validate();
18170     },
18171     /**
18172      * @property {Object} the last set data for the element
18173      */
18174     
18175     lastData : false,
18176     /**
18177      * Sets the value of the field based on a object which is related to the record format for the store.
18178      * @param {Object} value the value to set as. or false on reset?
18179      */
18180     setFromData : function(o){
18181         
18182         if(this.multiple){
18183             this.addItem(o);
18184             return;
18185         }
18186             
18187         var dv = ''; // display value
18188         var vv = ''; // value value..
18189         this.lastData = o;
18190         if (this.displayField) {
18191             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18192         } else {
18193             // this is an error condition!!!
18194             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18195         }
18196         
18197         if(this.valueField){
18198             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18199         }
18200         
18201         var close = this.closeTriggerEl();
18202         
18203         if(close){
18204             if(dv.length || vv * 1 > 0){
18205                 close.show() ;
18206                 this.blockFocus=true;
18207             } else {
18208                 close.hide();
18209             }             
18210         }
18211         
18212         if(this.hiddenField){
18213             this.hiddenField.dom.value = vv;
18214             
18215             this.lastSelectionText = dv;
18216             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18217             this.value = vv;
18218             return;
18219         }
18220         // no hidden field.. - we store the value in 'value', but still display
18221         // display field!!!!
18222         this.lastSelectionText = dv;
18223         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18224         this.value = vv;
18225         
18226         
18227         
18228     },
18229     // private
18230     reset : function(){
18231         // overridden so that last data is reset..
18232         
18233         if(this.multiple){
18234             this.clearItem();
18235             return;
18236         }
18237         
18238         this.setValue(this.originalValue);
18239         //this.clearInvalid();
18240         this.lastData = false;
18241         if (this.view) {
18242             this.view.clearSelections();
18243         }
18244         
18245         this.validate();
18246     },
18247     // private
18248     findRecord : function(prop, value){
18249         var record;
18250         if(this.store.getCount() > 0){
18251             this.store.each(function(r){
18252                 if(r.data[prop] == value){
18253                     record = r;
18254                     return false;
18255                 }
18256                 return true;
18257             });
18258         }
18259         return record;
18260     },
18261     
18262     getName: function()
18263     {
18264         // returns hidden if it's set..
18265         if (!this.rendered) {return ''};
18266         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18267         
18268     },
18269     // private
18270     onViewMove : function(e, t){
18271         this.inKeyMode = false;
18272     },
18273
18274     // private
18275     onViewOver : function(e, t){
18276         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18277             return;
18278         }
18279         var item = this.view.findItemFromChild(t);
18280         
18281         if(item){
18282             var index = this.view.indexOf(item);
18283             this.select(index, false);
18284         }
18285     },
18286
18287     // private
18288     onViewClick : function(view, doFocus, el, e)
18289     {
18290         var index = this.view.getSelectedIndexes()[0];
18291         
18292         var r = this.store.getAt(index);
18293         
18294         if(this.tickable){
18295             
18296             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18297                 return;
18298             }
18299             
18300             var rm = false;
18301             var _this = this;
18302             
18303             Roo.each(this.tickItems, function(v,k){
18304                 
18305                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18306                     Roo.log(v);
18307                     _this.tickItems.splice(k, 1);
18308                     
18309                     if(typeof(e) == 'undefined' && view == false){
18310                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18311                     }
18312                     
18313                     rm = true;
18314                     return;
18315                 }
18316             });
18317             
18318             if(rm){
18319                 return;
18320             }
18321             
18322             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18323                 this.tickItems.push(r.data);
18324             }
18325             
18326             if(typeof(e) == 'undefined' && view == false){
18327                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18328             }
18329                     
18330             return;
18331         }
18332         
18333         if(r){
18334             this.onSelect(r, index);
18335         }
18336         if(doFocus !== false && !this.blockFocus){
18337             this.inputEl().focus();
18338         }
18339     },
18340
18341     // private
18342     restrictHeight : function(){
18343         //this.innerList.dom.style.height = '';
18344         //var inner = this.innerList.dom;
18345         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18346         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18347         //this.list.beginUpdate();
18348         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18349         this.list.alignTo(this.inputEl(), this.listAlign);
18350         this.list.alignTo(this.inputEl(), this.listAlign);
18351         //this.list.endUpdate();
18352     },
18353
18354     // private
18355     onEmptyResults : function(){
18356         
18357         if(this.tickable && this.editable){
18358             this.hasFocus = false;
18359             this.restrictHeight();
18360             return;
18361         }
18362         
18363         this.collapse();
18364     },
18365
18366     /**
18367      * Returns true if the dropdown list is expanded, else false.
18368      */
18369     isExpanded : function(){
18370         return this.list.isVisible();
18371     },
18372
18373     /**
18374      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18376      * @param {String} value The data value of the item to select
18377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18378      * selected item if it is not currently in view (defaults to true)
18379      * @return {Boolean} True if the value matched an item in the list, else false
18380      */
18381     selectByValue : function(v, scrollIntoView){
18382         if(v !== undefined && v !== null){
18383             var r = this.findRecord(this.valueField || this.displayField, v);
18384             if(r){
18385                 this.select(this.store.indexOf(r), scrollIntoView);
18386                 return true;
18387             }
18388         }
18389         return false;
18390     },
18391
18392     /**
18393      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18395      * @param {Number} index The zero-based index of the list item to select
18396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18397      * selected item if it is not currently in view (defaults to true)
18398      */
18399     select : function(index, scrollIntoView){
18400         this.selectedIndex = index;
18401         this.view.select(index);
18402         if(scrollIntoView !== false){
18403             var el = this.view.getNode(index);
18404             /*
18405              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18406              */
18407             if(el){
18408                 this.list.scrollChildIntoView(el, false);
18409             }
18410         }
18411     },
18412
18413     // private
18414     selectNext : function(){
18415         var ct = this.store.getCount();
18416         if(ct > 0){
18417             if(this.selectedIndex == -1){
18418                 this.select(0);
18419             }else if(this.selectedIndex < ct-1){
18420                 this.select(this.selectedIndex+1);
18421             }
18422         }
18423     },
18424
18425     // private
18426     selectPrev : function(){
18427         var ct = this.store.getCount();
18428         if(ct > 0){
18429             if(this.selectedIndex == -1){
18430                 this.select(0);
18431             }else if(this.selectedIndex != 0){
18432                 this.select(this.selectedIndex-1);
18433             }
18434         }
18435     },
18436
18437     // private
18438     onKeyUp : function(e){
18439         if(this.editable !== false && !e.isSpecialKey()){
18440             this.lastKey = e.getKey();
18441             this.dqTask.delay(this.queryDelay);
18442         }
18443     },
18444
18445     // private
18446     validateBlur : function(){
18447         return !this.list || !this.list.isVisible();   
18448     },
18449
18450     // private
18451     initQuery : function(){
18452         
18453         var v = this.getRawValue();
18454         
18455         if(this.tickable && this.editable){
18456             v = this.tickableInputEl().getValue();
18457         }
18458         
18459         this.doQuery(v);
18460     },
18461
18462     // private
18463     doForce : function(){
18464         if(this.inputEl().dom.value.length > 0){
18465             this.inputEl().dom.value =
18466                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18467              
18468         }
18469     },
18470
18471     /**
18472      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18473      * query allowing the query action to be canceled if needed.
18474      * @param {String} query The SQL query to execute
18475      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18476      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18477      * saved in the current store (defaults to false)
18478      */
18479     doQuery : function(q, forceAll){
18480         
18481         if(q === undefined || q === null){
18482             q = '';
18483         }
18484         var qe = {
18485             query: q,
18486             forceAll: forceAll,
18487             combo: this,
18488             cancel:false
18489         };
18490         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18491             return false;
18492         }
18493         q = qe.query;
18494         
18495         forceAll = qe.forceAll;
18496         if(forceAll === true || (q.length >= this.minChars)){
18497             
18498             this.hasQuery = true;
18499             
18500             if(this.lastQuery != q || this.alwaysQuery){
18501                 this.lastQuery = q;
18502                 if(this.mode == 'local'){
18503                     this.selectedIndex = -1;
18504                     if(forceAll){
18505                         this.store.clearFilter();
18506                     }else{
18507                         
18508                         if(this.specialFilter){
18509                             this.fireEvent('specialfilter', this);
18510                             this.onLoad();
18511                             return;
18512                         }
18513                         
18514                         this.store.filter(this.displayField, q);
18515                     }
18516                     
18517                     this.store.fireEvent("datachanged", this.store);
18518                     
18519                     this.onLoad();
18520                     
18521                     
18522                 }else{
18523                     
18524                     this.store.baseParams[this.queryParam] = q;
18525                     
18526                     var options = {params : this.getParams(q)};
18527                     
18528                     if(this.loadNext){
18529                         options.add = true;
18530                         options.params.start = this.page * this.pageSize;
18531                     }
18532                     
18533                     this.store.load(options);
18534                     
18535                     /*
18536                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18537                      *  we should expand the list on onLoad
18538                      *  so command out it
18539                      */
18540 //                    this.expand();
18541                 }
18542             }else{
18543                 this.selectedIndex = -1;
18544                 this.onLoad();   
18545             }
18546         }
18547         
18548         this.loadNext = false;
18549     },
18550     
18551     // private
18552     getParams : function(q){
18553         var p = {};
18554         //p[this.queryParam] = q;
18555         
18556         if(this.pageSize){
18557             p.start = 0;
18558             p.limit = this.pageSize;
18559         }
18560         return p;
18561     },
18562
18563     /**
18564      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18565      */
18566     collapse : function(){
18567         if(!this.isExpanded()){
18568             return;
18569         }
18570         
18571         this.list.hide();
18572         
18573         this.hasFocus = false;
18574         
18575         if(this.tickable){
18576             this.okBtn.hide();
18577             this.cancelBtn.hide();
18578             this.trigger.show();
18579             
18580             if(this.editable){
18581                 this.tickableInputEl().dom.value = '';
18582                 this.tickableInputEl().blur();
18583             }
18584             
18585         }
18586         
18587         Roo.get(document).un('mousedown', this.collapseIf, this);
18588         Roo.get(document).un('mousewheel', this.collapseIf, this);
18589         if (!this.editable) {
18590             Roo.get(document).un('keydown', this.listKeyPress, this);
18591         }
18592         this.fireEvent('collapse', this);
18593         
18594         this.validate();
18595     },
18596
18597     // private
18598     collapseIf : function(e){
18599         var in_combo  = e.within(this.el);
18600         var in_list =  e.within(this.list);
18601         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18602         
18603         if (in_combo || in_list || is_list) {
18604             //e.stopPropagation();
18605             return;
18606         }
18607         
18608         if(this.tickable){
18609             this.onTickableFooterButtonClick(e, false, false);
18610         }
18611
18612         this.collapse();
18613         
18614     },
18615
18616     /**
18617      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18618      */
18619     expand : function(){
18620        
18621         if(this.isExpanded() || !this.hasFocus){
18622             return;
18623         }
18624         
18625         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18626         this.list.setWidth(lw);
18627         
18628         Roo.log('expand');
18629         
18630         this.list.show();
18631         
18632         this.restrictHeight();
18633         
18634         if(this.tickable){
18635             
18636             this.tickItems = Roo.apply([], this.item);
18637             
18638             this.okBtn.show();
18639             this.cancelBtn.show();
18640             this.trigger.hide();
18641             
18642             if(this.editable){
18643                 this.tickableInputEl().focus();
18644             }
18645             
18646         }
18647         
18648         Roo.get(document).on('mousedown', this.collapseIf, this);
18649         Roo.get(document).on('mousewheel', this.collapseIf, this);
18650         if (!this.editable) {
18651             Roo.get(document).on('keydown', this.listKeyPress, this);
18652         }
18653         
18654         this.fireEvent('expand', this);
18655     },
18656
18657     // private
18658     // Implements the default empty TriggerField.onTriggerClick function
18659     onTriggerClick : function(e)
18660     {
18661         Roo.log('trigger click');
18662         
18663         if(this.disabled || !this.triggerList){
18664             return;
18665         }
18666         
18667         this.page = 0;
18668         this.loadNext = false;
18669         
18670         if(this.isExpanded()){
18671             this.collapse();
18672             if (!this.blockFocus) {
18673                 this.inputEl().focus();
18674             }
18675             
18676         }else {
18677             this.hasFocus = true;
18678             if(this.triggerAction == 'all') {
18679                 this.doQuery(this.allQuery, true);
18680             } else {
18681                 this.doQuery(this.getRawValue());
18682             }
18683             if (!this.blockFocus) {
18684                 this.inputEl().focus();
18685             }
18686         }
18687     },
18688     
18689     onTickableTriggerClick : function(e)
18690     {
18691         if(this.disabled){
18692             return;
18693         }
18694         
18695         this.page = 0;
18696         this.loadNext = false;
18697         this.hasFocus = true;
18698         
18699         if(this.triggerAction == 'all') {
18700             this.doQuery(this.allQuery, true);
18701         } else {
18702             this.doQuery(this.getRawValue());
18703         }
18704     },
18705     
18706     onSearchFieldClick : function(e)
18707     {
18708         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18709             this.onTickableFooterButtonClick(e, false, false);
18710             return;
18711         }
18712         
18713         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18714             return;
18715         }
18716         
18717         this.page = 0;
18718         this.loadNext = false;
18719         this.hasFocus = true;
18720         
18721         if(this.triggerAction == 'all') {
18722             this.doQuery(this.allQuery, true);
18723         } else {
18724             this.doQuery(this.getRawValue());
18725         }
18726     },
18727     
18728     listKeyPress : function(e)
18729     {
18730         //Roo.log('listkeypress');
18731         // scroll to first matching element based on key pres..
18732         if (e.isSpecialKey()) {
18733             return false;
18734         }
18735         var k = String.fromCharCode(e.getKey()).toUpperCase();
18736         //Roo.log(k);
18737         var match  = false;
18738         var csel = this.view.getSelectedNodes();
18739         var cselitem = false;
18740         if (csel.length) {
18741             var ix = this.view.indexOf(csel[0]);
18742             cselitem  = this.store.getAt(ix);
18743             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18744                 cselitem = false;
18745             }
18746             
18747         }
18748         
18749         this.store.each(function(v) { 
18750             if (cselitem) {
18751                 // start at existing selection.
18752                 if (cselitem.id == v.id) {
18753                     cselitem = false;
18754                 }
18755                 return true;
18756             }
18757                 
18758             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18759                 match = this.store.indexOf(v);
18760                 return false;
18761             }
18762             return true;
18763         }, this);
18764         
18765         if (match === false) {
18766             return true; // no more action?
18767         }
18768         // scroll to?
18769         this.view.select(match);
18770         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18771         sn.scrollIntoView(sn.dom.parentNode, false);
18772     },
18773     
18774     onViewScroll : function(e, t){
18775         
18776         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){
18777             return;
18778         }
18779         
18780         this.hasQuery = true;
18781         
18782         this.loading = this.list.select('.loading', true).first();
18783         
18784         if(this.loading === null){
18785             this.list.createChild({
18786                 tag: 'div',
18787                 cls: 'loading roo-select2-more-results roo-select2-active',
18788                 html: 'Loading more results...'
18789             });
18790             
18791             this.loading = this.list.select('.loading', true).first();
18792             
18793             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18794             
18795             this.loading.hide();
18796         }
18797         
18798         this.loading.show();
18799         
18800         var _combo = this;
18801         
18802         this.page++;
18803         this.loadNext = true;
18804         
18805         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18806         
18807         return;
18808     },
18809     
18810     addItem : function(o)
18811     {   
18812         var dv = ''; // display value
18813         
18814         if (this.displayField) {
18815             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18816         } else {
18817             // this is an error condition!!!
18818             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18819         }
18820         
18821         if(!dv.length){
18822             return;
18823         }
18824         
18825         var choice = this.choices.createChild({
18826             tag: 'li',
18827             cls: 'roo-select2-search-choice',
18828             cn: [
18829                 {
18830                     tag: 'div',
18831                     html: dv
18832                 },
18833                 {
18834                     tag: 'a',
18835                     href: '#',
18836                     cls: 'roo-select2-search-choice-close fa fa-times',
18837                     tabindex: '-1'
18838                 }
18839             ]
18840             
18841         }, this.searchField);
18842         
18843         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18844         
18845         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18846         
18847         this.item.push(o);
18848         
18849         this.lastData = o;
18850         
18851         this.syncValue();
18852         
18853         this.inputEl().dom.value = '';
18854         
18855         this.validate();
18856     },
18857     
18858     onRemoveItem : function(e, _self, o)
18859     {
18860         e.preventDefault();
18861         
18862         this.lastItem = Roo.apply([], this.item);
18863         
18864         var index = this.item.indexOf(o.data) * 1;
18865         
18866         if( index < 0){
18867             Roo.log('not this item?!');
18868             return;
18869         }
18870         
18871         this.item.splice(index, 1);
18872         o.item.remove();
18873         
18874         this.syncValue();
18875         
18876         this.fireEvent('remove', this, e);
18877         
18878         this.validate();
18879         
18880     },
18881     
18882     syncValue : function()
18883     {
18884         if(!this.item.length){
18885             this.clearValue();
18886             return;
18887         }
18888             
18889         var value = [];
18890         var _this = this;
18891         Roo.each(this.item, function(i){
18892             if(_this.valueField){
18893                 value.push(i[_this.valueField]);
18894                 return;
18895             }
18896
18897             value.push(i);
18898         });
18899
18900         this.value = value.join(',');
18901
18902         if(this.hiddenField){
18903             this.hiddenField.dom.value = this.value;
18904         }
18905         
18906         this.store.fireEvent("datachanged", this.store);
18907         
18908         this.validate();
18909     },
18910     
18911     clearItem : function()
18912     {
18913         if(!this.multiple){
18914             return;
18915         }
18916         
18917         this.item = [];
18918         
18919         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18920            c.remove();
18921         });
18922         
18923         this.syncValue();
18924         
18925         this.validate();
18926         
18927         if(this.tickable && !Roo.isTouch){
18928             this.view.refresh();
18929         }
18930     },
18931     
18932     inputEl: function ()
18933     {
18934         if(Roo.isIOS && this.useNativeIOS){
18935             return this.el.select('select.roo-ios-select', true).first();
18936         }
18937         
18938         if(Roo.isTouch && this.mobileTouchView){
18939             return this.el.select('input.form-control',true).first();
18940         }
18941         
18942         if(this.tickable){
18943             return this.searchField;
18944         }
18945         
18946         return this.el.select('input.form-control',true).first();
18947     },
18948     
18949     onTickableFooterButtonClick : function(e, btn, el)
18950     {
18951         e.preventDefault();
18952         
18953         this.lastItem = Roo.apply([], this.item);
18954         
18955         if(btn && btn.name == 'cancel'){
18956             this.tickItems = Roo.apply([], this.item);
18957             this.collapse();
18958             return;
18959         }
18960         
18961         this.clearItem();
18962         
18963         var _this = this;
18964         
18965         Roo.each(this.tickItems, function(o){
18966             _this.addItem(o);
18967         });
18968         
18969         this.collapse();
18970         
18971     },
18972     
18973     validate : function()
18974     {
18975         if(this.getVisibilityEl().hasClass('hidden')){
18976             return true;
18977         }
18978         
18979         var v = this.getRawValue();
18980         
18981         if(this.multiple){
18982             v = this.getValue();
18983         }
18984         
18985         if(this.disabled || this.allowBlank || v.length){
18986             this.markValid();
18987             return true;
18988         }
18989         
18990         this.markInvalid();
18991         return false;
18992     },
18993     
18994     tickableInputEl : function()
18995     {
18996         if(!this.tickable || !this.editable){
18997             return this.inputEl();
18998         }
18999         
19000         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19001     },
19002     
19003     
19004     getAutoCreateTouchView : function()
19005     {
19006         var id = Roo.id();
19007         
19008         var cfg = {
19009             cls: 'form-group' //input-group
19010         };
19011         
19012         var input =  {
19013             tag: 'input',
19014             id : id,
19015             type : this.inputType,
19016             cls : 'form-control x-combo-noedit',
19017             autocomplete: 'new-password',
19018             placeholder : this.placeholder || '',
19019             readonly : true
19020         };
19021         
19022         if (this.name) {
19023             input.name = this.name;
19024         }
19025         
19026         if (this.size) {
19027             input.cls += ' input-' + this.size;
19028         }
19029         
19030         if (this.disabled) {
19031             input.disabled = true;
19032         }
19033         
19034         var inputblock = {
19035             cls : 'roo-combobox-wrap',
19036             cn : [
19037                 input
19038             ]
19039         };
19040         
19041         if(this.before){
19042             inputblock.cls += ' input-group';
19043             
19044             inputblock.cn.unshift({
19045                 tag :'span',
19046                 cls : 'input-group-addon input-group-prepend input-group-text',
19047                 html : this.before
19048             });
19049         }
19050         
19051         if(this.removable && !this.multiple){
19052             inputblock.cls += ' roo-removable';
19053             
19054             inputblock.cn.push({
19055                 tag: 'button',
19056                 html : 'x',
19057                 cls : 'roo-combo-removable-btn close'
19058             });
19059         }
19060
19061         if(this.hasFeedback && !this.allowBlank){
19062             
19063             inputblock.cls += ' has-feedback';
19064             
19065             inputblock.cn.push({
19066                 tag: 'span',
19067                 cls: 'glyphicon form-control-feedback'
19068             });
19069             
19070         }
19071         
19072         if (this.after) {
19073             
19074             inputblock.cls += (this.before) ? '' : ' input-group';
19075             
19076             inputblock.cn.push({
19077                 tag :'span',
19078                 cls : 'input-group-addon input-group-append input-group-text',
19079                 html : this.after
19080             });
19081         }
19082
19083         
19084         var ibwrap = inputblock;
19085         
19086         if(this.multiple){
19087             ibwrap = {
19088                 tag: 'ul',
19089                 cls: 'roo-select2-choices',
19090                 cn:[
19091                     {
19092                         tag: 'li',
19093                         cls: 'roo-select2-search-field',
19094                         cn: [
19095
19096                             inputblock
19097                         ]
19098                     }
19099                 ]
19100             };
19101         
19102             
19103         }
19104         
19105         var combobox = {
19106             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19107             cn: [
19108                 {
19109                     tag: 'input',
19110                     type : 'hidden',
19111                     cls: 'form-hidden-field'
19112                 },
19113                 ibwrap
19114             ]
19115         };
19116         
19117         if(!this.multiple && this.showToggleBtn){
19118             
19119             var caret = {
19120                 cls: 'caret'
19121             };
19122             
19123             if (this.caret != false) {
19124                 caret = {
19125                      tag: 'i',
19126                      cls: 'fa fa-' + this.caret
19127                 };
19128                 
19129             }
19130             
19131             combobox.cn.push({
19132                 tag :'span',
19133                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19134                 cn : [
19135                     Roo.bootstrap.version == 3 ? caret : '',
19136                     {
19137                         tag: 'span',
19138                         cls: 'combobox-clear',
19139                         cn  : [
19140                             {
19141                                 tag : 'i',
19142                                 cls: 'icon-remove'
19143                             }
19144                         ]
19145                     }
19146                 ]
19147
19148             })
19149         }
19150         
19151         if(this.multiple){
19152             combobox.cls += ' roo-select2-container-multi';
19153         }
19154         
19155         var required =  this.allowBlank ?  {
19156                     tag : 'i',
19157                     style: 'display: none'
19158                 } : {
19159                    tag : 'i',
19160                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19161                    tooltip : 'This field is required'
19162                 };
19163         
19164         var align = this.labelAlign || this.parentLabelAlign();
19165         
19166         if (align ==='left' && this.fieldLabel.length) {
19167
19168             cfg.cn = [
19169                 required,
19170                 {
19171                     tag: 'label',
19172                     cls : 'control-label col-form-label',
19173                     html : this.fieldLabel
19174
19175                 },
19176                 {
19177                     cls : 'roo-combobox-wrap ', 
19178                     cn: [
19179                         combobox
19180                     ]
19181                 }
19182             ];
19183             
19184             var labelCfg = cfg.cn[1];
19185             var contentCfg = cfg.cn[2];
19186             
19187
19188             if(this.indicatorpos == 'right'){
19189                 cfg.cn = [
19190                     {
19191                         tag: 'label',
19192                         'for' :  id,
19193                         cls : 'control-label col-form-label',
19194                         cn : [
19195                             {
19196                                 tag : 'span',
19197                                 html : this.fieldLabel
19198                             },
19199                             required
19200                         ]
19201                     },
19202                     {
19203                         cls : "roo-combobox-wrap ",
19204                         cn: [
19205                             combobox
19206                         ]
19207                     }
19208
19209                 ];
19210                 
19211                 labelCfg = cfg.cn[0];
19212                 contentCfg = cfg.cn[1];
19213             }
19214             
19215            
19216             
19217             if(this.labelWidth > 12){
19218                 labelCfg.style = "width: " + this.labelWidth + 'px';
19219             }
19220            
19221             if(this.labelWidth < 13 && this.labelmd == 0){
19222                 this.labelmd = this.labelWidth;
19223             }
19224             
19225             if(this.labellg > 0){
19226                 labelCfg.cls += ' col-lg-' + this.labellg;
19227                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19228             }
19229             
19230             if(this.labelmd > 0){
19231                 labelCfg.cls += ' col-md-' + this.labelmd;
19232                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19233             }
19234             
19235             if(this.labelsm > 0){
19236                 labelCfg.cls += ' col-sm-' + this.labelsm;
19237                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19238             }
19239             
19240             if(this.labelxs > 0){
19241                 labelCfg.cls += ' col-xs-' + this.labelxs;
19242                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19243             }
19244                 
19245                 
19246         } else if ( this.fieldLabel.length) {
19247             cfg.cn = [
19248                required,
19249                 {
19250                     tag: 'label',
19251                     cls : 'control-label',
19252                     html : this.fieldLabel
19253
19254                 },
19255                 {
19256                     cls : '', 
19257                     cn: [
19258                         combobox
19259                     ]
19260                 }
19261             ];
19262             
19263             if(this.indicatorpos == 'right'){
19264                 cfg.cn = [
19265                     {
19266                         tag: 'label',
19267                         cls : 'control-label',
19268                         html : this.fieldLabel,
19269                         cn : [
19270                             required
19271                         ]
19272                     },
19273                     {
19274                         cls : '', 
19275                         cn: [
19276                             combobox
19277                         ]
19278                     }
19279                 ];
19280             }
19281         } else {
19282             cfg.cn = combobox;    
19283         }
19284         
19285         
19286         var settings = this;
19287         
19288         ['xs','sm','md','lg'].map(function(size){
19289             if (settings[size]) {
19290                 cfg.cls += ' col-' + size + '-' + settings[size];
19291             }
19292         });
19293         
19294         return cfg;
19295     },
19296     
19297     initTouchView : function()
19298     {
19299         this.renderTouchView();
19300         
19301         this.touchViewEl.on('scroll', function(){
19302             this.el.dom.scrollTop = 0;
19303         }, this);
19304         
19305         this.originalValue = this.getValue();
19306         
19307         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19308         
19309         this.inputEl().on("click", this.showTouchView, this);
19310         if (this.triggerEl) {
19311             this.triggerEl.on("click", this.showTouchView, this);
19312         }
19313         
19314         
19315         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19316         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19317         
19318         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19319         
19320         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19321         this.store.on('load', this.onTouchViewLoad, this);
19322         this.store.on('loadexception', this.onTouchViewLoadException, this);
19323         
19324         if(this.hiddenName){
19325             
19326             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19327             
19328             this.hiddenField.dom.value =
19329                 this.hiddenValue !== undefined ? this.hiddenValue :
19330                 this.value !== undefined ? this.value : '';
19331         
19332             this.el.dom.removeAttribute('name');
19333             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19334         }
19335         
19336         if(this.multiple){
19337             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19338             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19339         }
19340         
19341         if(this.removable && !this.multiple){
19342             var close = this.closeTriggerEl();
19343             if(close){
19344                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19345                 close.on('click', this.removeBtnClick, this, close);
19346             }
19347         }
19348         /*
19349          * fix the bug in Safari iOS8
19350          */
19351         this.inputEl().on("focus", function(e){
19352             document.activeElement.blur();
19353         }, this);
19354         
19355         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19356         
19357         return;
19358         
19359         
19360     },
19361     
19362     renderTouchView : function()
19363     {
19364         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19365         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         
19367         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19368         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369         
19370         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19371         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         this.touchViewBodyEl.setStyle('overflow', 'auto');
19373         
19374         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19375         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376         
19377         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19378         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         
19380     },
19381     
19382     showTouchView : function()
19383     {
19384         if(this.disabled){
19385             return;
19386         }
19387         
19388         this.touchViewHeaderEl.hide();
19389
19390         if(this.modalTitle.length){
19391             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19392             this.touchViewHeaderEl.show();
19393         }
19394
19395         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19396         this.touchViewEl.show();
19397
19398         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19399         
19400         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19401         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19402
19403         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19404
19405         if(this.modalTitle.length){
19406             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19407         }
19408         
19409         this.touchViewBodyEl.setHeight(bodyHeight);
19410
19411         if(this.animate){
19412             var _this = this;
19413             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19414         }else{
19415             this.touchViewEl.addClass(['in','show']);
19416         }
19417         
19418         if(this._touchViewMask){
19419             Roo.get(document.body).addClass("x-body-masked");
19420             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19421             this._touchViewMask.setStyle('z-index', 10000);
19422             this._touchViewMask.addClass('show');
19423         }
19424         
19425         this.doTouchViewQuery();
19426         
19427     },
19428     
19429     hideTouchView : function()
19430     {
19431         this.touchViewEl.removeClass(['in','show']);
19432
19433         if(this.animate){
19434             var _this = this;
19435             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19436         }else{
19437             this.touchViewEl.setStyle('display', 'none');
19438         }
19439         
19440         if(this._touchViewMask){
19441             this._touchViewMask.removeClass('show');
19442             Roo.get(document.body).removeClass("x-body-masked");
19443         }
19444     },
19445     
19446     setTouchViewValue : function()
19447     {
19448         if(this.multiple){
19449             this.clearItem();
19450         
19451             var _this = this;
19452
19453             Roo.each(this.tickItems, function(o){
19454                 this.addItem(o);
19455             }, this);
19456         }
19457         
19458         this.hideTouchView();
19459     },
19460     
19461     doTouchViewQuery : function()
19462     {
19463         var qe = {
19464             query: '',
19465             forceAll: true,
19466             combo: this,
19467             cancel:false
19468         };
19469         
19470         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19471             return false;
19472         }
19473         
19474         if(!this.alwaysQuery || this.mode == 'local'){
19475             this.onTouchViewLoad();
19476             return;
19477         }
19478         
19479         this.store.load();
19480     },
19481     
19482     onTouchViewBeforeLoad : function(combo,opts)
19483     {
19484         return;
19485     },
19486
19487     // private
19488     onTouchViewLoad : function()
19489     {
19490         if(this.store.getCount() < 1){
19491             this.onTouchViewEmptyResults();
19492             return;
19493         }
19494         
19495         this.clearTouchView();
19496         
19497         var rawValue = this.getRawValue();
19498         
19499         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19500         
19501         this.tickItems = [];
19502         
19503         this.store.data.each(function(d, rowIndex){
19504             var row = this.touchViewListGroup.createChild(template);
19505             
19506             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19507                 row.addClass(d.data.cls);
19508             }
19509             
19510             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19511                 var cfg = {
19512                     data : d.data,
19513                     html : d.data[this.displayField]
19514                 };
19515                 
19516                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19517                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19518                 }
19519             }
19520             row.removeClass('selected');
19521             if(!this.multiple && this.valueField &&
19522                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19523             {
19524                 // radio buttons..
19525                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19526                 row.addClass('selected');
19527             }
19528             
19529             if(this.multiple && this.valueField &&
19530                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19531             {
19532                 
19533                 // checkboxes...
19534                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19535                 this.tickItems.push(d.data);
19536             }
19537             
19538             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19539             
19540         }, this);
19541         
19542         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19543         
19544         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19545
19546         if(this.modalTitle.length){
19547             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19548         }
19549
19550         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19551         
19552         if(this.mobile_restrict_height && listHeight < bodyHeight){
19553             this.touchViewBodyEl.setHeight(listHeight);
19554         }
19555         
19556         var _this = this;
19557         
19558         if(firstChecked && listHeight > bodyHeight){
19559             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19560         }
19561         
19562     },
19563     
19564     onTouchViewLoadException : function()
19565     {
19566         this.hideTouchView();
19567     },
19568     
19569     onTouchViewEmptyResults : function()
19570     {
19571         this.clearTouchView();
19572         
19573         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19574         
19575         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19576         
19577     },
19578     
19579     clearTouchView : function()
19580     {
19581         this.touchViewListGroup.dom.innerHTML = '';
19582     },
19583     
19584     onTouchViewClick : function(e, el, o)
19585     {
19586         e.preventDefault();
19587         
19588         var row = o.row;
19589         var rowIndex = o.rowIndex;
19590         
19591         var r = this.store.getAt(rowIndex);
19592         
19593         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19594             
19595             if(!this.multiple){
19596                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19597                     c.dom.removeAttribute('checked');
19598                 }, this);
19599
19600                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19601
19602                 this.setFromData(r.data);
19603
19604                 var close = this.closeTriggerEl();
19605
19606                 if(close){
19607                     close.show();
19608                 }
19609
19610                 this.hideTouchView();
19611
19612                 this.fireEvent('select', this, r, rowIndex);
19613
19614                 return;
19615             }
19616
19617             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19618                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19619                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19620                 return;
19621             }
19622
19623             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19624             this.addItem(r.data);
19625             this.tickItems.push(r.data);
19626         }
19627     },
19628     
19629     getAutoCreateNativeIOS : function()
19630     {
19631         var cfg = {
19632             cls: 'form-group' //input-group,
19633         };
19634         
19635         var combobox =  {
19636             tag: 'select',
19637             cls : 'roo-ios-select'
19638         };
19639         
19640         if (this.name) {
19641             combobox.name = this.name;
19642         }
19643         
19644         if (this.disabled) {
19645             combobox.disabled = true;
19646         }
19647         
19648         var settings = this;
19649         
19650         ['xs','sm','md','lg'].map(function(size){
19651             if (settings[size]) {
19652                 cfg.cls += ' col-' + size + '-' + settings[size];
19653             }
19654         });
19655         
19656         cfg.cn = combobox;
19657         
19658         return cfg;
19659         
19660     },
19661     
19662     initIOSView : function()
19663     {
19664         this.store.on('load', this.onIOSViewLoad, this);
19665         
19666         return;
19667     },
19668     
19669     onIOSViewLoad : function()
19670     {
19671         if(this.store.getCount() < 1){
19672             return;
19673         }
19674         
19675         this.clearIOSView();
19676         
19677         if(this.allowBlank) {
19678             
19679             var default_text = '-- SELECT --';
19680             
19681             if(this.placeholder.length){
19682                 default_text = this.placeholder;
19683             }
19684             
19685             if(this.emptyTitle.length){
19686                 default_text += ' - ' + this.emptyTitle + ' -';
19687             }
19688             
19689             var opt = this.inputEl().createChild({
19690                 tag: 'option',
19691                 value : 0,
19692                 html : default_text
19693             });
19694             
19695             var o = {};
19696             o[this.valueField] = 0;
19697             o[this.displayField] = default_text;
19698             
19699             this.ios_options.push({
19700                 data : o,
19701                 el : opt
19702             });
19703             
19704         }
19705         
19706         this.store.data.each(function(d, rowIndex){
19707             
19708             var html = '';
19709             
19710             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19711                 html = d.data[this.displayField];
19712             }
19713             
19714             var value = '';
19715             
19716             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19717                 value = d.data[this.valueField];
19718             }
19719             
19720             var option = {
19721                 tag: 'option',
19722                 value : value,
19723                 html : html
19724             };
19725             
19726             if(this.value == d.data[this.valueField]){
19727                 option['selected'] = true;
19728             }
19729             
19730             var opt = this.inputEl().createChild(option);
19731             
19732             this.ios_options.push({
19733                 data : d.data,
19734                 el : opt
19735             });
19736             
19737         }, this);
19738         
19739         this.inputEl().on('change', function(){
19740            this.fireEvent('select', this);
19741         }, this);
19742         
19743     },
19744     
19745     clearIOSView: function()
19746     {
19747         this.inputEl().dom.innerHTML = '';
19748         
19749         this.ios_options = [];
19750     },
19751     
19752     setIOSValue: function(v)
19753     {
19754         this.value = v;
19755         
19756         if(!this.ios_options){
19757             return;
19758         }
19759         
19760         Roo.each(this.ios_options, function(opts){
19761            
19762            opts.el.dom.removeAttribute('selected');
19763            
19764            if(opts.data[this.valueField] != v){
19765                return;
19766            }
19767            
19768            opts.el.dom.setAttribute('selected', true);
19769            
19770         }, this);
19771     }
19772
19773     /** 
19774     * @cfg {Boolean} grow 
19775     * @hide 
19776     */
19777     /** 
19778     * @cfg {Number} growMin 
19779     * @hide 
19780     */
19781     /** 
19782     * @cfg {Number} growMax 
19783     * @hide 
19784     */
19785     /**
19786      * @hide
19787      * @method autoSize
19788      */
19789 });
19790
19791 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19792     
19793     header : {
19794         tag: 'div',
19795         cls: 'modal-header',
19796         cn: [
19797             {
19798                 tag: 'h4',
19799                 cls: 'modal-title'
19800             }
19801         ]
19802     },
19803     
19804     body : {
19805         tag: 'div',
19806         cls: 'modal-body',
19807         cn: [
19808             {
19809                 tag: 'ul',
19810                 cls: 'list-group'
19811             }
19812         ]
19813     },
19814     
19815     listItemRadio : {
19816         tag: 'li',
19817         cls: 'list-group-item',
19818         cn: [
19819             {
19820                 tag: 'span',
19821                 cls: 'roo-combobox-list-group-item-value'
19822             },
19823             {
19824                 tag: 'div',
19825                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19826                 cn: [
19827                     {
19828                         tag: 'input',
19829                         type: 'radio'
19830                     },
19831                     {
19832                         tag: 'label'
19833                     }
19834                 ]
19835             }
19836         ]
19837     },
19838     
19839     listItemCheckbox : {
19840         tag: 'li',
19841         cls: 'list-group-item',
19842         cn: [
19843             {
19844                 tag: 'span',
19845                 cls: 'roo-combobox-list-group-item-value'
19846             },
19847             {
19848                 tag: 'div',
19849                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19850                 cn: [
19851                     {
19852                         tag: 'input',
19853                         type: 'checkbox'
19854                     },
19855                     {
19856                         tag: 'label'
19857                     }
19858                 ]
19859             }
19860         ]
19861     },
19862     
19863     emptyResult : {
19864         tag: 'div',
19865         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19866     },
19867     
19868     footer : {
19869         tag: 'div',
19870         cls: 'modal-footer',
19871         cn: [
19872             {
19873                 tag: 'div',
19874                 cls: 'row',
19875                 cn: [
19876                     {
19877                         tag: 'div',
19878                         cls: 'col-xs-6 text-left',
19879                         cn: {
19880                             tag: 'button',
19881                             cls: 'btn btn-danger roo-touch-view-cancel',
19882                             html: 'Cancel'
19883                         }
19884                     },
19885                     {
19886                         tag: 'div',
19887                         cls: 'col-xs-6 text-right',
19888                         cn: {
19889                             tag: 'button',
19890                             cls: 'btn btn-success roo-touch-view-ok',
19891                             html: 'OK'
19892                         }
19893                     }
19894                 ]
19895             }
19896         ]
19897         
19898     }
19899 });
19900
19901 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19902     
19903     touchViewTemplate : {
19904         tag: 'div',
19905         cls: 'modal fade roo-combobox-touch-view',
19906         cn: [
19907             {
19908                 tag: 'div',
19909                 cls: 'modal-dialog',
19910                 style : 'position:fixed', // we have to fix position....
19911                 cn: [
19912                     {
19913                         tag: 'div',
19914                         cls: 'modal-content',
19915                         cn: [
19916                             Roo.bootstrap.form.ComboBox.header,
19917                             Roo.bootstrap.form.ComboBox.body,
19918                             Roo.bootstrap.form.ComboBox.footer
19919                         ]
19920                     }
19921                 ]
19922             }
19923         ]
19924     }
19925 });/*
19926  * Based on:
19927  * Ext JS Library 1.1.1
19928  * Copyright(c) 2006-2007, Ext JS, LLC.
19929  *
19930  * Originally Released Under LGPL - original licence link has changed is not relivant.
19931  *
19932  * Fork - LGPL
19933  * <script type="text/javascript">
19934  */
19935
19936 /**
19937  * @class Roo.View
19938  * @extends Roo.util.Observable
19939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19940  * This class also supports single and multi selection modes. <br>
19941  * Create a data model bound view:
19942  <pre><code>
19943  var store = new Roo.data.Store(...);
19944
19945  var view = new Roo.View({
19946     el : "my-element",
19947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19948  
19949     singleSelect: true,
19950     selectedClass: "ydataview-selected",
19951     store: store
19952  });
19953
19954  // listen for node click?
19955  view.on("click", function(vw, index, node, e){
19956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19957  });
19958
19959  // load XML data
19960  dataModel.load("foobar.xml");
19961  </code></pre>
19962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19963  * <br><br>
19964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19966  * 
19967  * Note: old style constructor is still suported (container, template, config)
19968  * 
19969  * @constructor
19970  * Create a new View
19971  * @param {Object} config The config object
19972  * 
19973  */
19974 Roo.View = function(config, depreciated_tpl, depreciated_config){
19975     
19976     this.parent = false;
19977     
19978     if (typeof(depreciated_tpl) == 'undefined') {
19979         // new way.. - universal constructor.
19980         Roo.apply(this, config);
19981         this.el  = Roo.get(this.el);
19982     } else {
19983         // old format..
19984         this.el  = Roo.get(config);
19985         this.tpl = depreciated_tpl;
19986         Roo.apply(this, depreciated_config);
19987     }
19988     this.wrapEl  = this.el.wrap().wrap();
19989     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19990     
19991     
19992     if(typeof(this.tpl) == "string"){
19993         this.tpl = new Roo.Template(this.tpl);
19994     } else {
19995         // support xtype ctors..
19996         this.tpl = new Roo.factory(this.tpl, Roo);
19997     }
19998     
19999     
20000     this.tpl.compile();
20001     
20002     /** @private */
20003     this.addEvents({
20004         /**
20005          * @event beforeclick
20006          * Fires before a click is processed. Returns false to cancel the default action.
20007          * @param {Roo.View} this
20008          * @param {Number} index The index of the target node
20009          * @param {HTMLElement} node The target node
20010          * @param {Roo.EventObject} e The raw event object
20011          */
20012             "beforeclick" : true,
20013         /**
20014          * @event click
20015          * Fires when a template node is clicked.
20016          * @param {Roo.View} this
20017          * @param {Number} index The index of the target node
20018          * @param {HTMLElement} node The target node
20019          * @param {Roo.EventObject} e The raw event object
20020          */
20021             "click" : true,
20022         /**
20023          * @event dblclick
20024          * Fires when a template node is double clicked.
20025          * @param {Roo.View} this
20026          * @param {Number} index The index of the target node
20027          * @param {HTMLElement} node The target node
20028          * @param {Roo.EventObject} e The raw event object
20029          */
20030             "dblclick" : true,
20031         /**
20032          * @event contextmenu
20033          * Fires when a template node is right clicked.
20034          * @param {Roo.View} this
20035          * @param {Number} index The index of the target node
20036          * @param {HTMLElement} node The target node
20037          * @param {Roo.EventObject} e The raw event object
20038          */
20039             "contextmenu" : true,
20040         /**
20041          * @event selectionchange
20042          * Fires when the selected nodes change.
20043          * @param {Roo.View} this
20044          * @param {Array} selections Array of the selected nodes
20045          */
20046             "selectionchange" : true,
20047     
20048         /**
20049          * @event beforeselect
20050          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20051          * @param {Roo.View} this
20052          * @param {HTMLElement} node The node to be selected
20053          * @param {Array} selections Array of currently selected nodes
20054          */
20055             "beforeselect" : true,
20056         /**
20057          * @event preparedata
20058          * Fires on every row to render, to allow you to change the data.
20059          * @param {Roo.View} this
20060          * @param {Object} data to be rendered (change this)
20061          */
20062           "preparedata" : true
20063           
20064           
20065         });
20066
20067
20068
20069     this.el.on({
20070         "click": this.onClick,
20071         "dblclick": this.onDblClick,
20072         "contextmenu": this.onContextMenu,
20073         scope:this
20074     });
20075
20076     this.selections = [];
20077     this.nodes = [];
20078     this.cmp = new Roo.CompositeElementLite([]);
20079     if(this.store){
20080         this.store = Roo.factory(this.store, Roo.data);
20081         this.setStore(this.store, true);
20082     }
20083     
20084     if ( this.footer && this.footer.xtype) {
20085            
20086          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20087         
20088         this.footer.dataSource = this.store;
20089         this.footer.container = fctr;
20090         this.footer = Roo.factory(this.footer, Roo);
20091         fctr.insertFirst(this.el);
20092         
20093         // this is a bit insane - as the paging toolbar seems to detach the el..
20094 //        dom.parentNode.parentNode.parentNode
20095          // they get detached?
20096     }
20097     
20098     
20099     Roo.View.superclass.constructor.call(this);
20100     
20101     
20102 };
20103
20104 Roo.extend(Roo.View, Roo.util.Observable, {
20105     
20106      /**
20107      * @cfg {Roo.data.Store} store Data store to load data from.
20108      */
20109     store : false,
20110     
20111     /**
20112      * @cfg {String|Roo.Element} el The container element.
20113      */
20114     el : '',
20115     
20116     /**
20117      * @cfg {String|Roo.Template} tpl The template used by this View 
20118      */
20119     tpl : false,
20120     /**
20121      * @cfg {String} dataName the named area of the template to use as the data area
20122      *                          Works with domtemplates roo-name="name"
20123      */
20124     dataName: false,
20125     /**
20126      * @cfg {String} selectedClass The css class to add to selected nodes
20127      */
20128     selectedClass : "x-view-selected",
20129      /**
20130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20131      */
20132     emptyText : "",
20133     
20134     /**
20135      * @cfg {String} text to display on mask (default Loading)
20136      */
20137     mask : false,
20138     /**
20139      * @cfg {Boolean} multiSelect Allow multiple selection
20140      */
20141     multiSelect : false,
20142     /**
20143      * @cfg {Boolean} singleSelect Allow single selection
20144      */
20145     singleSelect:  false,
20146     
20147     /**
20148      * @cfg {Boolean} toggleSelect - selecting 
20149      */
20150     toggleSelect : false,
20151     
20152     /**
20153      * @cfg {Boolean} tickable - selecting 
20154      */
20155     tickable : false,
20156     
20157     /**
20158      * Returns the element this view is bound to.
20159      * @return {Roo.Element}
20160      */
20161     getEl : function(){
20162         return this.wrapEl;
20163     },
20164     
20165     
20166
20167     /**
20168      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20169      */
20170     refresh : function(){
20171         //Roo.log('refresh');
20172         var t = this.tpl;
20173         
20174         // if we are using something like 'domtemplate', then
20175         // the what gets used is:
20176         // t.applySubtemplate(NAME, data, wrapping data..)
20177         // the outer template then get' applied with
20178         //     the store 'extra data'
20179         // and the body get's added to the
20180         //      roo-name="data" node?
20181         //      <span class='roo-tpl-{name}'></span> ?????
20182         
20183         
20184         
20185         this.clearSelections();
20186         this.el.update("");
20187         var html = [];
20188         var records = this.store.getRange();
20189         if(records.length < 1) {
20190             
20191             // is this valid??  = should it render a template??
20192             
20193             this.el.update(this.emptyText);
20194             return;
20195         }
20196         var el = this.el;
20197         if (this.dataName) {
20198             this.el.update(t.apply(this.store.meta)); //????
20199             el = this.el.child('.roo-tpl-' + this.dataName);
20200         }
20201         
20202         for(var i = 0, len = records.length; i < len; i++){
20203             var data = this.prepareData(records[i].data, i, records[i]);
20204             this.fireEvent("preparedata", this, data, i, records[i]);
20205             
20206             var d = Roo.apply({}, data);
20207             
20208             if(this.tickable){
20209                 Roo.apply(d, {'roo-id' : Roo.id()});
20210                 
20211                 var _this = this;
20212             
20213                 Roo.each(this.parent.item, function(item){
20214                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20215                         return;
20216                     }
20217                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20218                 });
20219             }
20220             
20221             html[html.length] = Roo.util.Format.trim(
20222                 this.dataName ?
20223                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20224                     t.apply(d)
20225             );
20226         }
20227         
20228         
20229         
20230         el.update(html.join(""));
20231         this.nodes = el.dom.childNodes;
20232         this.updateIndexes(0);
20233     },
20234     
20235
20236     /**
20237      * Function to override to reformat the data that is sent to
20238      * the template for each node.
20239      * DEPRICATED - use the preparedata event handler.
20240      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20241      * a JSON object for an UpdateManager bound view).
20242      */
20243     prepareData : function(data, index, record)
20244     {
20245         this.fireEvent("preparedata", this, data, index, record);
20246         return data;
20247     },
20248
20249     onUpdate : function(ds, record){
20250         // Roo.log('on update');   
20251         this.clearSelections();
20252         var index = this.store.indexOf(record);
20253         var n = this.nodes[index];
20254         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20255         n.parentNode.removeChild(n);
20256         this.updateIndexes(index, index);
20257     },
20258
20259     
20260     
20261 // --------- FIXME     
20262     onAdd : function(ds, records, index)
20263     {
20264         //Roo.log(['on Add', ds, records, index] );        
20265         this.clearSelections();
20266         if(this.nodes.length == 0){
20267             this.refresh();
20268             return;
20269         }
20270         var n = this.nodes[index];
20271         for(var i = 0, len = records.length; i < len; i++){
20272             var d = this.prepareData(records[i].data, i, records[i]);
20273             if(n){
20274                 this.tpl.insertBefore(n, d);
20275             }else{
20276                 
20277                 this.tpl.append(this.el, d);
20278             }
20279         }
20280         this.updateIndexes(index);
20281     },
20282
20283     onRemove : function(ds, record, index){
20284        // Roo.log('onRemove');
20285         this.clearSelections();
20286         var el = this.dataName  ?
20287             this.el.child('.roo-tpl-' + this.dataName) :
20288             this.el; 
20289         
20290         el.dom.removeChild(this.nodes[index]);
20291         this.updateIndexes(index);
20292     },
20293
20294     /**
20295      * Refresh an individual node.
20296      * @param {Number} index
20297      */
20298     refreshNode : function(index){
20299         this.onUpdate(this.store, this.store.getAt(index));
20300     },
20301
20302     updateIndexes : function(startIndex, endIndex){
20303         var ns = this.nodes;
20304         startIndex = startIndex || 0;
20305         endIndex = endIndex || ns.length - 1;
20306         for(var i = startIndex; i <= endIndex; i++){
20307             ns[i].nodeIndex = i;
20308         }
20309     },
20310
20311     /**
20312      * Changes the data store this view uses and refresh the view.
20313      * @param {Store} store
20314      */
20315     setStore : function(store, initial){
20316         if(!initial && this.store){
20317             this.store.un("datachanged", this.refresh);
20318             this.store.un("add", this.onAdd);
20319             this.store.un("remove", this.onRemove);
20320             this.store.un("update", this.onUpdate);
20321             this.store.un("clear", this.refresh);
20322             this.store.un("beforeload", this.onBeforeLoad);
20323             this.store.un("load", this.onLoad);
20324             this.store.un("loadexception", this.onLoad);
20325         }
20326         if(store){
20327           
20328             store.on("datachanged", this.refresh, this);
20329             store.on("add", this.onAdd, this);
20330             store.on("remove", this.onRemove, this);
20331             store.on("update", this.onUpdate, this);
20332             store.on("clear", this.refresh, this);
20333             store.on("beforeload", this.onBeforeLoad, this);
20334             store.on("load", this.onLoad, this);
20335             store.on("loadexception", this.onLoad, this);
20336         }
20337         
20338         if(store){
20339             this.refresh();
20340         }
20341     },
20342     /**
20343      * onbeforeLoad - masks the loading area.
20344      *
20345      */
20346     onBeforeLoad : function(store,opts)
20347     {
20348          //Roo.log('onBeforeLoad');   
20349         if (!opts.add) {
20350             this.el.update("");
20351         }
20352         this.el.mask(this.mask ? this.mask : "Loading" ); 
20353     },
20354     onLoad : function ()
20355     {
20356         this.el.unmask();
20357     },
20358     
20359
20360     /**
20361      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20362      * @param {HTMLElement} node
20363      * @return {HTMLElement} The template node
20364      */
20365     findItemFromChild : function(node){
20366         var el = this.dataName  ?
20367             this.el.child('.roo-tpl-' + this.dataName,true) :
20368             this.el.dom; 
20369         
20370         if(!node || node.parentNode == el){
20371                     return node;
20372             }
20373             var p = node.parentNode;
20374             while(p && p != el){
20375             if(p.parentNode == el){
20376                 return p;
20377             }
20378             p = p.parentNode;
20379         }
20380             return null;
20381     },
20382
20383     /** @ignore */
20384     onClick : function(e){
20385         var item = this.findItemFromChild(e.getTarget());
20386         if(item){
20387             var index = this.indexOf(item);
20388             if(this.onItemClick(item, index, e) !== false){
20389                 this.fireEvent("click", this, index, item, e);
20390             }
20391         }else{
20392             this.clearSelections();
20393         }
20394     },
20395
20396     /** @ignore */
20397     onContextMenu : function(e){
20398         var item = this.findItemFromChild(e.getTarget());
20399         if(item){
20400             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20401         }
20402     },
20403
20404     /** @ignore */
20405     onDblClick : function(e){
20406         var item = this.findItemFromChild(e.getTarget());
20407         if(item){
20408             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20409         }
20410     },
20411
20412     onItemClick : function(item, index, e)
20413     {
20414         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20415             return false;
20416         }
20417         if (this.toggleSelect) {
20418             var m = this.isSelected(item) ? 'unselect' : 'select';
20419             //Roo.log(m);
20420             var _t = this;
20421             _t[m](item, true, false);
20422             return true;
20423         }
20424         if(this.multiSelect || this.singleSelect){
20425             if(this.multiSelect && e.shiftKey && this.lastSelection){
20426                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20427             }else{
20428                 this.select(item, this.multiSelect && e.ctrlKey);
20429                 this.lastSelection = item;
20430             }
20431             
20432             if(!this.tickable){
20433                 e.preventDefault();
20434             }
20435             
20436         }
20437         return true;
20438     },
20439
20440     /**
20441      * Get the number of selected nodes.
20442      * @return {Number}
20443      */
20444     getSelectionCount : function(){
20445         return this.selections.length;
20446     },
20447
20448     /**
20449      * Get the currently selected nodes.
20450      * @return {Array} An array of HTMLElements
20451      */
20452     getSelectedNodes : function(){
20453         return this.selections;
20454     },
20455
20456     /**
20457      * Get the indexes of the selected nodes.
20458      * @return {Array}
20459      */
20460     getSelectedIndexes : function(){
20461         var indexes = [], s = this.selections;
20462         for(var i = 0, len = s.length; i < len; i++){
20463             indexes.push(s[i].nodeIndex);
20464         }
20465         return indexes;
20466     },
20467
20468     /**
20469      * Clear all selections
20470      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20471      */
20472     clearSelections : function(suppressEvent){
20473         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20474             this.cmp.elements = this.selections;
20475             this.cmp.removeClass(this.selectedClass);
20476             this.selections = [];
20477             if(!suppressEvent){
20478                 this.fireEvent("selectionchange", this, this.selections);
20479             }
20480         }
20481     },
20482
20483     /**
20484      * Returns true if the passed node is selected
20485      * @param {HTMLElement/Number} node The node or node index
20486      * @return {Boolean}
20487      */
20488     isSelected : function(node){
20489         var s = this.selections;
20490         if(s.length < 1){
20491             return false;
20492         }
20493         node = this.getNode(node);
20494         return s.indexOf(node) !== -1;
20495     },
20496
20497     /**
20498      * Selects nodes.
20499      * @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
20500      * @param {Boolean} keepExisting (optional) true to keep existing selections
20501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20502      */
20503     select : function(nodeInfo, keepExisting, suppressEvent){
20504         if(nodeInfo instanceof Array){
20505             if(!keepExisting){
20506                 this.clearSelections(true);
20507             }
20508             for(var i = 0, len = nodeInfo.length; i < len; i++){
20509                 this.select(nodeInfo[i], true, true);
20510             }
20511             return;
20512         } 
20513         var node = this.getNode(nodeInfo);
20514         if(!node || this.isSelected(node)){
20515             return; // already selected.
20516         }
20517         if(!keepExisting){
20518             this.clearSelections(true);
20519         }
20520         
20521         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20522             Roo.fly(node).addClass(this.selectedClass);
20523             this.selections.push(node);
20524             if(!suppressEvent){
20525                 this.fireEvent("selectionchange", this, this.selections);
20526             }
20527         }
20528         
20529         
20530     },
20531       /**
20532      * Unselects nodes.
20533      * @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
20534      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20536      */
20537     unselect : function(nodeInfo, keepExisting, suppressEvent)
20538     {
20539         if(nodeInfo instanceof Array){
20540             Roo.each(this.selections, function(s) {
20541                 this.unselect(s, nodeInfo);
20542             }, this);
20543             return;
20544         }
20545         var node = this.getNode(nodeInfo);
20546         if(!node || !this.isSelected(node)){
20547             //Roo.log("not selected");
20548             return; // not selected.
20549         }
20550         // fireevent???
20551         var ns = [];
20552         Roo.each(this.selections, function(s) {
20553             if (s == node ) {
20554                 Roo.fly(node).removeClass(this.selectedClass);
20555
20556                 return;
20557             }
20558             ns.push(s);
20559         },this);
20560         
20561         this.selections= ns;
20562         this.fireEvent("selectionchange", this, this.selections);
20563     },
20564
20565     /**
20566      * Gets a template node.
20567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568      * @return {HTMLElement} The node or null if it wasn't found
20569      */
20570     getNode : function(nodeInfo){
20571         if(typeof nodeInfo == "string"){
20572             return document.getElementById(nodeInfo);
20573         }else if(typeof nodeInfo == "number"){
20574             return this.nodes[nodeInfo];
20575         }
20576         return nodeInfo;
20577     },
20578
20579     /**
20580      * Gets a range template nodes.
20581      * @param {Number} startIndex
20582      * @param {Number} endIndex
20583      * @return {Array} An array of nodes
20584      */
20585     getNodes : function(start, end){
20586         var ns = this.nodes;
20587         start = start || 0;
20588         end = typeof end == "undefined" ? ns.length - 1 : end;
20589         var nodes = [];
20590         if(start <= end){
20591             for(var i = start; i <= end; i++){
20592                 nodes.push(ns[i]);
20593             }
20594         } else{
20595             for(var i = start; i >= end; i--){
20596                 nodes.push(ns[i]);
20597             }
20598         }
20599         return nodes;
20600     },
20601
20602     /**
20603      * Finds the index of the passed node
20604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20605      * @return {Number} The index of the node or -1
20606      */
20607     indexOf : function(node){
20608         node = this.getNode(node);
20609         if(typeof node.nodeIndex == "number"){
20610             return node.nodeIndex;
20611         }
20612         var ns = this.nodes;
20613         for(var i = 0, len = ns.length; i < len; i++){
20614             if(ns[i] == node){
20615                 return i;
20616             }
20617         }
20618         return -1;
20619     }
20620 });
20621 /*
20622  * - LGPL
20623  *
20624  * based on jquery fullcalendar
20625  * 
20626  */
20627
20628 Roo.bootstrap = Roo.bootstrap || {};
20629 /**
20630  * @class Roo.bootstrap.Calendar
20631  * @extends Roo.bootstrap.Component
20632  * Bootstrap Calendar class
20633  * @cfg {Boolean} loadMask (true|false) default false
20634  * @cfg {Object} header generate the user specific header of the calendar, default false
20635
20636  * @constructor
20637  * Create a new Container
20638  * @param {Object} config The config object
20639  */
20640
20641
20642
20643 Roo.bootstrap.Calendar = function(config){
20644     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20645      this.addEvents({
20646         /**
20647              * @event select
20648              * Fires when a date is selected
20649              * @param {DatePicker} this
20650              * @param {Date} date The selected date
20651              */
20652         'select': true,
20653         /**
20654              * @event monthchange
20655              * Fires when the displayed month changes 
20656              * @param {DatePicker} this
20657              * @param {Date} date The selected month
20658              */
20659         'monthchange': true,
20660         /**
20661              * @event evententer
20662              * Fires when mouse over an event
20663              * @param {Calendar} this
20664              * @param {event} Event
20665              */
20666         'evententer': true,
20667         /**
20668              * @event eventleave
20669              * Fires when the mouse leaves an
20670              * @param {Calendar} this
20671              * @param {event}
20672              */
20673         'eventleave': true,
20674         /**
20675              * @event eventclick
20676              * Fires when the mouse click an
20677              * @param {Calendar} this
20678              * @param {event}
20679              */
20680         'eventclick': true
20681         
20682     });
20683
20684 };
20685
20686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20687     
20688           /**
20689      * @cfg {Roo.data.Store} store
20690      * The data source for the calendar
20691      */
20692         store : false,
20693      /**
20694      * @cfg {Number} startDay
20695      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20696      */
20697     startDay : 0,
20698     
20699     loadMask : false,
20700     
20701     header : false,
20702       
20703     getAutoCreate : function(){
20704         
20705         
20706         var fc_button = function(name, corner, style, content ) {
20707             return Roo.apply({},{
20708                 tag : 'span',
20709                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20710                          (corner.length ?
20711                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20712                             ''
20713                         ),
20714                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20715                 unselectable: 'on'
20716             });
20717         };
20718         
20719         var header = {};
20720         
20721         if(!this.header){
20722             header = {
20723                 tag : 'table',
20724                 cls : 'fc-header',
20725                 style : 'width:100%',
20726                 cn : [
20727                     {
20728                         tag: 'tr',
20729                         cn : [
20730                             {
20731                                 tag : 'td',
20732                                 cls : 'fc-header-left',
20733                                 cn : [
20734                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20735                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20736                                     { tag: 'span', cls: 'fc-header-space' },
20737                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20738
20739
20740                                 ]
20741                             },
20742
20743                             {
20744                                 tag : 'td',
20745                                 cls : 'fc-header-center',
20746                                 cn : [
20747                                     {
20748                                         tag: 'span',
20749                                         cls: 'fc-header-title',
20750                                         cn : {
20751                                             tag: 'H2',
20752                                             html : 'month / year'
20753                                         }
20754                                     }
20755
20756                                 ]
20757                             },
20758                             {
20759                                 tag : 'td',
20760                                 cls : 'fc-header-right',
20761                                 cn : [
20762                               /*      fc_button('month', 'left', '', 'month' ),
20763                                     fc_button('week', '', '', 'week' ),
20764                                     fc_button('day', 'right', '', 'day' )
20765                                 */    
20766
20767                                 ]
20768                             }
20769
20770                         ]
20771                     }
20772                 ]
20773             };
20774         }
20775         
20776         header = this.header;
20777         
20778        
20779         var cal_heads = function() {
20780             var ret = [];
20781             // fixme - handle this.
20782             
20783             for (var i =0; i < Date.dayNames.length; i++) {
20784                 var d = Date.dayNames[i];
20785                 ret.push({
20786                     tag: 'th',
20787                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20788                     html : d.substring(0,3)
20789                 });
20790                 
20791             }
20792             ret[0].cls += ' fc-first';
20793             ret[6].cls += ' fc-last';
20794             return ret;
20795         };
20796         var cal_cell = function(n) {
20797             return  {
20798                 tag: 'td',
20799                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20800                 cn : [
20801                     {
20802                         cn : [
20803                             {
20804                                 cls: 'fc-day-number',
20805                                 html: 'D'
20806                             },
20807                             {
20808                                 cls: 'fc-day-content',
20809                              
20810                                 cn : [
20811                                      {
20812                                         style: 'position: relative;' // height: 17px;
20813                                     }
20814                                 ]
20815                             }
20816                             
20817                             
20818                         ]
20819                     }
20820                 ]
20821                 
20822             }
20823         };
20824         var cal_rows = function() {
20825             
20826             var ret = [];
20827             for (var r = 0; r < 6; r++) {
20828                 var row= {
20829                     tag : 'tr',
20830                     cls : 'fc-week',
20831                     cn : []
20832                 };
20833                 
20834                 for (var i =0; i < Date.dayNames.length; i++) {
20835                     var d = Date.dayNames[i];
20836                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20837
20838                 }
20839                 row.cn[0].cls+=' fc-first';
20840                 row.cn[0].cn[0].style = 'min-height:90px';
20841                 row.cn[6].cls+=' fc-last';
20842                 ret.push(row);
20843                 
20844             }
20845             ret[0].cls += ' fc-first';
20846             ret[4].cls += ' fc-prev-last';
20847             ret[5].cls += ' fc-last';
20848             return ret;
20849             
20850         };
20851         
20852         var cal_table = {
20853             tag: 'table',
20854             cls: 'fc-border-separate',
20855             style : 'width:100%',
20856             cellspacing  : 0,
20857             cn : [
20858                 { 
20859                     tag: 'thead',
20860                     cn : [
20861                         { 
20862                             tag: 'tr',
20863                             cls : 'fc-first fc-last',
20864                             cn : cal_heads()
20865                         }
20866                     ]
20867                 },
20868                 { 
20869                     tag: 'tbody',
20870                     cn : cal_rows()
20871                 }
20872                   
20873             ]
20874         };
20875          
20876          var cfg = {
20877             cls : 'fc fc-ltr',
20878             cn : [
20879                 header,
20880                 {
20881                     cls : 'fc-content',
20882                     style : "position: relative;",
20883                     cn : [
20884                         {
20885                             cls : 'fc-view fc-view-month fc-grid',
20886                             style : 'position: relative',
20887                             unselectable : 'on',
20888                             cn : [
20889                                 {
20890                                     cls : 'fc-event-container',
20891                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20892                                 },
20893                                 cal_table
20894                             ]
20895                         }
20896                     ]
20897     
20898                 }
20899            ] 
20900             
20901         };
20902         
20903          
20904         
20905         return cfg;
20906     },
20907     
20908     
20909     initEvents : function()
20910     {
20911         if(!this.store){
20912             throw "can not find store for calendar";
20913         }
20914         
20915         var mark = {
20916             tag: "div",
20917             cls:"x-dlg-mask",
20918             style: "text-align:center",
20919             cn: [
20920                 {
20921                     tag: "div",
20922                     style: "background-color:white;width:50%;margin:250 auto",
20923                     cn: [
20924                         {
20925                             tag: "img",
20926                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20927                         },
20928                         {
20929                             tag: "span",
20930                             html: "Loading"
20931                         }
20932                         
20933                     ]
20934                 }
20935             ]
20936         };
20937         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20938         
20939         var size = this.el.select('.fc-content', true).first().getSize();
20940         this.maskEl.setSize(size.width, size.height);
20941         this.maskEl.enableDisplayMode("block");
20942         if(!this.loadMask){
20943             this.maskEl.hide();
20944         }
20945         
20946         this.store = Roo.factory(this.store, Roo.data);
20947         this.store.on('load', this.onLoad, this);
20948         this.store.on('beforeload', this.onBeforeLoad, this);
20949         
20950         this.resize();
20951         
20952         this.cells = this.el.select('.fc-day',true);
20953         //Roo.log(this.cells);
20954         this.textNodes = this.el.query('.fc-day-number');
20955         this.cells.addClassOnOver('fc-state-hover');
20956         
20957         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20958         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20959         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20960         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20961         
20962         this.on('monthchange', this.onMonthChange, this);
20963         
20964         this.update(new Date().clearTime());
20965     },
20966     
20967     resize : function() {
20968         var sz  = this.el.getSize();
20969         
20970         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20971         this.el.select('.fc-day-content div',true).setHeight(34);
20972     },
20973     
20974     
20975     // private
20976     showPrevMonth : function(e){
20977         this.update(this.activeDate.add("mo", -1));
20978     },
20979     showToday : function(e){
20980         this.update(new Date().clearTime());
20981     },
20982     // private
20983     showNextMonth : function(e){
20984         this.update(this.activeDate.add("mo", 1));
20985     },
20986
20987     // private
20988     showPrevYear : function(){
20989         this.update(this.activeDate.add("y", -1));
20990     },
20991
20992     // private
20993     showNextYear : function(){
20994         this.update(this.activeDate.add("y", 1));
20995     },
20996
20997     
20998    // private
20999     update : function(date)
21000     {
21001         var vd = this.activeDate;
21002         this.activeDate = date;
21003 //        if(vd && this.el){
21004 //            var t = date.getTime();
21005 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21006 //                Roo.log('using add remove');
21007 //                
21008 //                this.fireEvent('monthchange', this, date);
21009 //                
21010 //                this.cells.removeClass("fc-state-highlight");
21011 //                this.cells.each(function(c){
21012 //                   if(c.dateValue == t){
21013 //                       c.addClass("fc-state-highlight");
21014 //                       setTimeout(function(){
21015 //                            try{c.dom.firstChild.focus();}catch(e){}
21016 //                       }, 50);
21017 //                       return false;
21018 //                   }
21019 //                   return true;
21020 //                });
21021 //                return;
21022 //            }
21023 //        }
21024         
21025         var days = date.getDaysInMonth();
21026         
21027         var firstOfMonth = date.getFirstDateOfMonth();
21028         var startingPos = firstOfMonth.getDay()-this.startDay;
21029         
21030         if(startingPos < this.startDay){
21031             startingPos += 7;
21032         }
21033         
21034         var pm = date.add(Date.MONTH, -1);
21035         var prevStart = pm.getDaysInMonth()-startingPos;
21036 //        
21037         this.cells = this.el.select('.fc-day',true);
21038         this.textNodes = this.el.query('.fc-day-number');
21039         this.cells.addClassOnOver('fc-state-hover');
21040         
21041         var cells = this.cells.elements;
21042         var textEls = this.textNodes;
21043         
21044         Roo.each(cells, function(cell){
21045             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21046         });
21047         
21048         days += startingPos;
21049
21050         // convert everything to numbers so it's fast
21051         var day = 86400000;
21052         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21053         //Roo.log(d);
21054         //Roo.log(pm);
21055         //Roo.log(prevStart);
21056         
21057         var today = new Date().clearTime().getTime();
21058         var sel = date.clearTime().getTime();
21059         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21060         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21061         var ddMatch = this.disabledDatesRE;
21062         var ddText = this.disabledDatesText;
21063         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21064         var ddaysText = this.disabledDaysText;
21065         var format = this.format;
21066         
21067         var setCellClass = function(cal, cell){
21068             cell.row = 0;
21069             cell.events = [];
21070             cell.more = [];
21071             //Roo.log('set Cell Class');
21072             cell.title = "";
21073             var t = d.getTime();
21074             
21075             //Roo.log(d);
21076             
21077             cell.dateValue = t;
21078             if(t == today){
21079                 cell.className += " fc-today";
21080                 cell.className += " fc-state-highlight";
21081                 cell.title = cal.todayText;
21082             }
21083             if(t == sel){
21084                 // disable highlight in other month..
21085                 //cell.className += " fc-state-highlight";
21086                 
21087             }
21088             // disabling
21089             if(t < min) {
21090                 cell.className = " fc-state-disabled";
21091                 cell.title = cal.minText;
21092                 return;
21093             }
21094             if(t > max) {
21095                 cell.className = " fc-state-disabled";
21096                 cell.title = cal.maxText;
21097                 return;
21098             }
21099             if(ddays){
21100                 if(ddays.indexOf(d.getDay()) != -1){
21101                     cell.title = ddaysText;
21102                     cell.className = " fc-state-disabled";
21103                 }
21104             }
21105             if(ddMatch && format){
21106                 var fvalue = d.dateFormat(format);
21107                 if(ddMatch.test(fvalue)){
21108                     cell.title = ddText.replace("%0", fvalue);
21109                     cell.className = " fc-state-disabled";
21110                 }
21111             }
21112             
21113             if (!cell.initialClassName) {
21114                 cell.initialClassName = cell.dom.className;
21115             }
21116             
21117             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21118         };
21119
21120         var i = 0;
21121         
21122         for(; i < startingPos; i++) {
21123             textEls[i].innerHTML = (++prevStart);
21124             d.setDate(d.getDate()+1);
21125             
21126             cells[i].className = "fc-past fc-other-month";
21127             setCellClass(this, cells[i]);
21128         }
21129         
21130         var intDay = 0;
21131         
21132         for(; i < days; i++){
21133             intDay = i - startingPos + 1;
21134             textEls[i].innerHTML = (intDay);
21135             d.setDate(d.getDate()+1);
21136             
21137             cells[i].className = ''; // "x-date-active";
21138             setCellClass(this, cells[i]);
21139         }
21140         var extraDays = 0;
21141         
21142         for(; i < 42; i++) {
21143             textEls[i].innerHTML = (++extraDays);
21144             d.setDate(d.getDate()+1);
21145             
21146             cells[i].className = "fc-future fc-other-month";
21147             setCellClass(this, cells[i]);
21148         }
21149         
21150         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21151         
21152         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21153         
21154         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21155         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21156         
21157         if(totalRows != 6){
21158             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21159             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21160         }
21161         
21162         this.fireEvent('monthchange', this, date);
21163         
21164         
21165         /*
21166         if(!this.internalRender){
21167             var main = this.el.dom.firstChild;
21168             var w = main.offsetWidth;
21169             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21170             Roo.fly(main).setWidth(w);
21171             this.internalRender = true;
21172             // opera does not respect the auto grow header center column
21173             // then, after it gets a width opera refuses to recalculate
21174             // without a second pass
21175             if(Roo.isOpera && !this.secondPass){
21176                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21177                 this.secondPass = true;
21178                 this.update.defer(10, this, [date]);
21179             }
21180         }
21181         */
21182         
21183     },
21184     
21185     findCell : function(dt) {
21186         dt = dt.clearTime().getTime();
21187         var ret = false;
21188         this.cells.each(function(c){
21189             //Roo.log("check " +c.dateValue + '?=' + dt);
21190             if(c.dateValue == dt){
21191                 ret = c;
21192                 return false;
21193             }
21194             return true;
21195         });
21196         
21197         return ret;
21198     },
21199     
21200     findCells : function(ev) {
21201         var s = ev.start.clone().clearTime().getTime();
21202        // Roo.log(s);
21203         var e= ev.end.clone().clearTime().getTime();
21204        // Roo.log(e);
21205         var ret = [];
21206         this.cells.each(function(c){
21207              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21208             
21209             if(c.dateValue > e){
21210                 return ;
21211             }
21212             if(c.dateValue < s){
21213                 return ;
21214             }
21215             ret.push(c);
21216         });
21217         
21218         return ret;    
21219     },
21220     
21221 //    findBestRow: function(cells)
21222 //    {
21223 //        var ret = 0;
21224 //        
21225 //        for (var i =0 ; i < cells.length;i++) {
21226 //            ret  = Math.max(cells[i].rows || 0,ret);
21227 //        }
21228 //        return ret;
21229 //        
21230 //    },
21231     
21232     
21233     addItem : function(ev)
21234     {
21235         // look for vertical location slot in
21236         var cells = this.findCells(ev);
21237         
21238 //        ev.row = this.findBestRow(cells);
21239         
21240         // work out the location.
21241         
21242         var crow = false;
21243         var rows = [];
21244         for(var i =0; i < cells.length; i++) {
21245             
21246             cells[i].row = cells[0].row;
21247             
21248             if(i == 0){
21249                 cells[i].row = cells[i].row + 1;
21250             }
21251             
21252             if (!crow) {
21253                 crow = {
21254                     start : cells[i],
21255                     end :  cells[i]
21256                 };
21257                 continue;
21258             }
21259             if (crow.start.getY() == cells[i].getY()) {
21260                 // on same row.
21261                 crow.end = cells[i];
21262                 continue;
21263             }
21264             // different row.
21265             rows.push(crow);
21266             crow = {
21267                 start: cells[i],
21268                 end : cells[i]
21269             };
21270             
21271         }
21272         
21273         rows.push(crow);
21274         ev.els = [];
21275         ev.rows = rows;
21276         ev.cells = cells;
21277         
21278         cells[0].events.push(ev);
21279         
21280         this.calevents.push(ev);
21281     },
21282     
21283     clearEvents: function() {
21284         
21285         if(!this.calevents){
21286             return;
21287         }
21288         
21289         Roo.each(this.cells.elements, function(c){
21290             c.row = 0;
21291             c.events = [];
21292             c.more = [];
21293         });
21294         
21295         Roo.each(this.calevents, function(e) {
21296             Roo.each(e.els, function(el) {
21297                 el.un('mouseenter' ,this.onEventEnter, this);
21298                 el.un('mouseleave' ,this.onEventLeave, this);
21299                 el.remove();
21300             },this);
21301         },this);
21302         
21303         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21304             e.remove();
21305         });
21306         
21307     },
21308     
21309     renderEvents: function()
21310     {   
21311         var _this = this;
21312         
21313         this.cells.each(function(c) {
21314             
21315             if(c.row < 5){
21316                 return;
21317             }
21318             
21319             var ev = c.events;
21320             
21321             var r = 4;
21322             if(c.row != c.events.length){
21323                 r = 4 - (4 - (c.row - c.events.length));
21324             }
21325             
21326             c.events = ev.slice(0, r);
21327             c.more = ev.slice(r);
21328             
21329             if(c.more.length && c.more.length == 1){
21330                 c.events.push(c.more.pop());
21331             }
21332             
21333             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21334             
21335         });
21336             
21337         this.cells.each(function(c) {
21338             
21339             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21340             
21341             
21342             for (var e = 0; e < c.events.length; e++){
21343                 var ev = c.events[e];
21344                 var rows = ev.rows;
21345                 
21346                 for(var i = 0; i < rows.length; i++) {
21347                 
21348                     // how many rows should it span..
21349
21350                     var  cfg = {
21351                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21352                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21353
21354                         unselectable : "on",
21355                         cn : [
21356                             {
21357                                 cls: 'fc-event-inner',
21358                                 cn : [
21359     //                                {
21360     //                                  tag:'span',
21361     //                                  cls: 'fc-event-time',
21362     //                                  html : cells.length > 1 ? '' : ev.time
21363     //                                },
21364                                     {
21365                                       tag:'span',
21366                                       cls: 'fc-event-title',
21367                                       html : String.format('{0}', ev.title)
21368                                     }
21369
21370
21371                                 ]
21372                             },
21373                             {
21374                                 cls: 'ui-resizable-handle ui-resizable-e',
21375                                 html : '&nbsp;&nbsp;&nbsp'
21376                             }
21377
21378                         ]
21379                     };
21380
21381                     if (i == 0) {
21382                         cfg.cls += ' fc-event-start';
21383                     }
21384                     if ((i+1) == rows.length) {
21385                         cfg.cls += ' fc-event-end';
21386                     }
21387
21388                     var ctr = _this.el.select('.fc-event-container',true).first();
21389                     var cg = ctr.createChild(cfg);
21390
21391                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21392                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21393
21394                     var r = (c.more.length) ? 1 : 0;
21395                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21396                     cg.setWidth(ebox.right - sbox.x -2);
21397
21398                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21399                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21400                     cg.on('click', _this.onEventClick, _this, ev);
21401
21402                     ev.els.push(cg);
21403                     
21404                 }
21405                 
21406             }
21407             
21408             
21409             if(c.more.length){
21410                 var  cfg = {
21411                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21412                     style : 'position: absolute',
21413                     unselectable : "on",
21414                     cn : [
21415                         {
21416                             cls: 'fc-event-inner',
21417                             cn : [
21418                                 {
21419                                   tag:'span',
21420                                   cls: 'fc-event-title',
21421                                   html : 'More'
21422                                 }
21423
21424
21425                             ]
21426                         },
21427                         {
21428                             cls: 'ui-resizable-handle ui-resizable-e',
21429                             html : '&nbsp;&nbsp;&nbsp'
21430                         }
21431
21432                     ]
21433                 };
21434
21435                 var ctr = _this.el.select('.fc-event-container',true).first();
21436                 var cg = ctr.createChild(cfg);
21437
21438                 var sbox = c.select('.fc-day-content',true).first().getBox();
21439                 var ebox = c.select('.fc-day-content',true).first().getBox();
21440                 //Roo.log(cg);
21441                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21442                 cg.setWidth(ebox.right - sbox.x -2);
21443
21444                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21445                 
21446             }
21447             
21448         });
21449         
21450         
21451         
21452     },
21453     
21454     onEventEnter: function (e, el,event,d) {
21455         this.fireEvent('evententer', this, el, event);
21456     },
21457     
21458     onEventLeave: function (e, el,event,d) {
21459         this.fireEvent('eventleave', this, el, event);
21460     },
21461     
21462     onEventClick: function (e, el,event,d) {
21463         this.fireEvent('eventclick', this, el, event);
21464     },
21465     
21466     onMonthChange: function () {
21467         this.store.load();
21468     },
21469     
21470     onMoreEventClick: function(e, el, more)
21471     {
21472         var _this = this;
21473         
21474         this.calpopover.placement = 'right';
21475         this.calpopover.setTitle('More');
21476         
21477         this.calpopover.setContent('');
21478         
21479         var ctr = this.calpopover.el.select('.popover-content', true).first();
21480         
21481         Roo.each(more, function(m){
21482             var cfg = {
21483                 cls : 'fc-event-hori fc-event-draggable',
21484                 html : m.title
21485             };
21486             var cg = ctr.createChild(cfg);
21487             
21488             cg.on('click', _this.onEventClick, _this, m);
21489         });
21490         
21491         this.calpopover.show(el);
21492         
21493         
21494     },
21495     
21496     onLoad: function () 
21497     {   
21498         this.calevents = [];
21499         var cal = this;
21500         
21501         if(this.store.getCount() > 0){
21502             this.store.data.each(function(d){
21503                cal.addItem({
21504                     id : d.data.id,
21505                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21506                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21507                     time : d.data.start_time,
21508                     title : d.data.title,
21509                     description : d.data.description,
21510                     venue : d.data.venue
21511                 });
21512             });
21513         }
21514         
21515         this.renderEvents();
21516         
21517         if(this.calevents.length && this.loadMask){
21518             this.maskEl.hide();
21519         }
21520     },
21521     
21522     onBeforeLoad: function()
21523     {
21524         this.clearEvents();
21525         if(this.loadMask){
21526             this.maskEl.show();
21527         }
21528     }
21529 });
21530
21531  
21532  /*
21533  * - LGPL
21534  *
21535  * element
21536  * 
21537  */
21538
21539 /**
21540  * @class Roo.bootstrap.Popover
21541  * @extends Roo.bootstrap.Component
21542  * @parent none builder
21543  * @children Roo.bootstrap.Component
21544  * Bootstrap Popover class
21545  * @cfg {String} html contents of the popover   (or false to use children..)
21546  * @cfg {String} title of popover (or false to hide)
21547  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21548  * @cfg {String} trigger click || hover (or false to trigger manually)
21549  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21550  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21551  *      - if false and it has a 'parent' then it will be automatically added to that element
21552  *      - if string - Roo.get  will be called 
21553  * @cfg {Number} delay - delay before showing
21554  
21555  * @constructor
21556  * Create a new Popover
21557  * @param {Object} config The config object
21558  */
21559
21560 Roo.bootstrap.Popover = function(config){
21561     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21562     
21563     this.addEvents({
21564         // raw events
21565          /**
21566          * @event show
21567          * After the popover show
21568          * 
21569          * @param {Roo.bootstrap.Popover} this
21570          */
21571         "show" : true,
21572         /**
21573          * @event hide
21574          * After the popover hide
21575          * 
21576          * @param {Roo.bootstrap.Popover} this
21577          */
21578         "hide" : true
21579     });
21580 };
21581
21582 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21583     
21584     title: false,
21585     html: false,
21586     
21587     placement : 'right',
21588     trigger : 'hover', // hover
21589     modal : false,
21590     delay : 0,
21591     
21592     over: false,
21593     
21594     can_build_overlaid : false,
21595     
21596     maskEl : false, // the mask element
21597     headerEl : false,
21598     contentEl : false,
21599     alignEl : false, // when show is called with an element - this get's stored.
21600     
21601     getChildContainer : function()
21602     {
21603         return this.contentEl;
21604         
21605     },
21606     getPopoverHeader : function()
21607     {
21608         this.title = true; // flag not to hide it..
21609         this.headerEl.addClass('p-0');
21610         return this.headerEl
21611     },
21612     
21613     
21614     getAutoCreate : function(){
21615          
21616         var cfg = {
21617            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21618            style: 'display:block',
21619            cn : [
21620                 {
21621                     cls : 'arrow'
21622                 },
21623                 {
21624                     cls : 'popover-inner ',
21625                     cn : [
21626                         {
21627                             tag: 'h3',
21628                             cls: 'popover-title popover-header',
21629                             html : this.title === false ? '' : this.title
21630                         },
21631                         {
21632                             cls : 'popover-content popover-body '  + (this.cls || ''),
21633                             html : this.html || ''
21634                         }
21635                     ]
21636                     
21637                 }
21638            ]
21639         };
21640         
21641         return cfg;
21642     },
21643     /**
21644      * @param {string} the title
21645      */
21646     setTitle: function(str)
21647     {
21648         this.title = str;
21649         if (this.el) {
21650             this.headerEl.dom.innerHTML = str;
21651         }
21652         
21653     },
21654     /**
21655      * @param {string} the body content
21656      */
21657     setContent: function(str)
21658     {
21659         this.html = str;
21660         if (this.contentEl) {
21661             this.contentEl.dom.innerHTML = str;
21662         }
21663         
21664     },
21665     // as it get's added to the bottom of the page.
21666     onRender : function(ct, position)
21667     {
21668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21669         
21670         
21671         
21672         if(!this.el){
21673             var cfg = Roo.apply({},  this.getAutoCreate());
21674             cfg.id = Roo.id();
21675             
21676             if (this.cls) {
21677                 cfg.cls += ' ' + this.cls;
21678             }
21679             if (this.style) {
21680                 cfg.style = this.style;
21681             }
21682             //Roo.log("adding to ");
21683             this.el = Roo.get(document.body).createChild(cfg, position);
21684 //            Roo.log(this.el);
21685         }
21686         
21687         this.contentEl = this.el.select('.popover-content',true).first();
21688         this.headerEl =  this.el.select('.popover-title',true).first();
21689         
21690         var nitems = [];
21691         if(typeof(this.items) != 'undefined'){
21692             var items = this.items;
21693             delete this.items;
21694
21695             for(var i =0;i < items.length;i++) {
21696                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21697             }
21698         }
21699
21700         this.items = nitems;
21701         
21702         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21703         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21704         
21705         
21706         
21707         this.initEvents();
21708     },
21709     
21710     resizeMask : function()
21711     {
21712         this.maskEl.setSize(
21713             Roo.lib.Dom.getViewWidth(true),
21714             Roo.lib.Dom.getViewHeight(true)
21715         );
21716     },
21717     
21718     initEvents : function()
21719     {
21720         
21721         if (!this.modal) { 
21722             Roo.bootstrap.Popover.register(this);
21723         }
21724          
21725         this.arrowEl = this.el.select('.arrow',true).first();
21726         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21727         this.el.enableDisplayMode('block');
21728         this.el.hide();
21729  
21730         
21731         if (this.over === false && !this.parent()) {
21732             return; 
21733         }
21734         if (this.triggers === false) {
21735             return;
21736         }
21737          
21738         // support parent
21739         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21740         var triggers = this.trigger ? this.trigger.split(' ') : [];
21741         Roo.each(triggers, function(trigger) {
21742         
21743             if (trigger == 'click') {
21744                 on_el.on('click', this.toggle, this);
21745             } else if (trigger != 'manual') {
21746                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21747                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21748       
21749                 on_el.on(eventIn  ,this.enter, this);
21750                 on_el.on(eventOut, this.leave, this);
21751             }
21752         }, this);
21753     },
21754     
21755     
21756     // private
21757     timeout : null,
21758     hoverState : null,
21759     
21760     toggle : function () {
21761         this.hoverState == 'in' ? this.leave() : this.enter();
21762     },
21763     
21764     enter : function () {
21765         
21766         clearTimeout(this.timeout);
21767     
21768         this.hoverState = 'in';
21769     
21770         if (!this.delay || !this.delay.show) {
21771             this.show();
21772             return;
21773         }
21774         var _t = this;
21775         this.timeout = setTimeout(function () {
21776             if (_t.hoverState == 'in') {
21777                 _t.show();
21778             }
21779         }, this.delay.show)
21780     },
21781     
21782     leave : function() {
21783         clearTimeout(this.timeout);
21784     
21785         this.hoverState = 'out';
21786     
21787         if (!this.delay || !this.delay.hide) {
21788             this.hide();
21789             return;
21790         }
21791         var _t = this;
21792         this.timeout = setTimeout(function () {
21793             if (_t.hoverState == 'out') {
21794                 _t.hide();
21795             }
21796         }, this.delay.hide)
21797     },
21798     
21799     /**
21800      * update the position of the dialog
21801      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21802      * 
21803      *
21804      */
21805     
21806     doAlign : function()
21807     {
21808         
21809         if (this.alignEl) {
21810             this.updatePosition(this.placement, true);
21811              
21812         } else {
21813             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21814             var es = this.el.getSize();
21815             var x = Roo.lib.Dom.getViewWidth()/2;
21816             var y = Roo.lib.Dom.getViewHeight()/2;
21817             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21818             
21819         }
21820
21821          
21822          
21823         
21824         
21825     },
21826     
21827     /**
21828      * Show the popover
21829      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21830      * @param {string} (left|right|top|bottom) position
21831      */
21832     show : function (on_el, placement)
21833     {
21834         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21835         on_el = on_el || false; // default to false
21836          
21837         if (!on_el) {
21838             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21839                 on_el = this.parent().el;
21840             } else if (this.over) {
21841                 on_el = Roo.get(this.over);
21842             }
21843             
21844         }
21845         
21846         this.alignEl = Roo.get( on_el );
21847
21848         if (!this.el) {
21849             this.render(document.body);
21850         }
21851         
21852         
21853          
21854         
21855         if (this.title === false) {
21856             this.headerEl.hide();
21857         }
21858         
21859        
21860         this.el.show();
21861         this.el.dom.style.display = 'block';
21862          
21863         this.doAlign();
21864         
21865         //var arrow = this.el.select('.arrow',true).first();
21866         //arrow.set(align[2], 
21867         
21868         this.el.addClass('in');
21869         
21870          
21871         
21872         this.hoverState = 'in';
21873         
21874         if (this.modal) {
21875             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21876             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877             this.maskEl.dom.style.display = 'block';
21878             this.maskEl.addClass('show');
21879         }
21880         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21881  
21882         this.fireEvent('show', this);
21883         
21884     },
21885     /**
21886      * fire this manually after loading a grid in the table for example
21887      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21888      * @param {Boolean} try and move it if we cant get right position.
21889      */
21890     updatePosition : function(placement, try_move)
21891     {
21892         // allow for calling with no parameters
21893         placement = placement   ? placement :  this.placement;
21894         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21895         
21896         this.el.removeClass([
21897             'fade','top','bottom', 'left', 'right','in',
21898             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21899         ]);
21900         this.el.addClass(placement + ' bs-popover-' + placement);
21901         
21902         if (!this.alignEl ) {
21903             return false;
21904         }
21905         
21906         switch (placement) {
21907             case 'right':
21908                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21909                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21910                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21911                     //normal display... or moved up/down.
21912                     this.el.setXY(offset);
21913                     var xy = this.alignEl.getAnchorXY('tr', false);
21914                     xy[0]+=2;xy[1]+=5;
21915                     this.arrowEl.setXY(xy);
21916                     return true;
21917                 }
21918                 // continue through...
21919                 return this.updatePosition('left', false);
21920                 
21921             
21922             case 'left':
21923                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21924                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21925                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21926                     //normal display... or moved up/down.
21927                     this.el.setXY(offset);
21928                     var xy = this.alignEl.getAnchorXY('tl', false);
21929                     xy[0]-=10;xy[1]+=5; // << fix me
21930                     this.arrowEl.setXY(xy);
21931                     return true;
21932                 }
21933                 // call self...
21934                 return this.updatePosition('right', false);
21935             
21936             case 'top':
21937                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21938                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21939                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21940                     //normal display... or moved up/down.
21941                     this.el.setXY(offset);
21942                     var xy = this.alignEl.getAnchorXY('t', false);
21943                     xy[1]-=10; // << fix me
21944                     this.arrowEl.setXY(xy);
21945                     return true;
21946                 }
21947                 // fall through
21948                return this.updatePosition('bottom', false);
21949             
21950             case 'bottom':
21951                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21952                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21953                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21954                     //normal display... or moved up/down.
21955                     this.el.setXY(offset);
21956                     var xy = this.alignEl.getAnchorXY('b', false);
21957                      xy[1]+=2; // << fix me
21958                     this.arrowEl.setXY(xy);
21959                     return true;
21960                 }
21961                 // fall through
21962                 return this.updatePosition('top', false);
21963                 
21964             
21965         }
21966         
21967         
21968         return false;
21969     },
21970     
21971     hide : function()
21972     {
21973         this.el.setXY([0,0]);
21974         this.el.removeClass('in');
21975         this.el.hide();
21976         this.hoverState = null;
21977         this.maskEl.hide(); // always..
21978         this.fireEvent('hide', this);
21979     }
21980     
21981 });
21982
21983
21984 Roo.apply(Roo.bootstrap.Popover, {
21985
21986     alignment : {
21987         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21988         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21989         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21990         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21991     },
21992     
21993     zIndex : 20001,
21994
21995     clickHander : false,
21996     
21997     
21998
21999     onMouseDown : function(e)
22000     {
22001         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22002             /// what is nothing is showing..
22003             this.hideAll();
22004         }
22005          
22006     },
22007     
22008     
22009     popups : [],
22010     
22011     register : function(popup)
22012     {
22013         if (!Roo.bootstrap.Popover.clickHandler) {
22014             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22015         }
22016         // hide other popups.
22017         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22018         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22019         this.hideAll(); //<< why?
22020         //this.popups.push(popup);
22021     },
22022     hideAll : function()
22023     {
22024         this.popups.forEach(function(p) {
22025             p.hide();
22026         });
22027     },
22028     onShow : function() {
22029         Roo.bootstrap.Popover.popups.push(this);
22030     },
22031     onHide : function() {
22032         Roo.bootstrap.Popover.popups.remove(this);
22033     } 
22034
22035 });
22036 /**
22037  * @class Roo.bootstrap.PopoverNav
22038  * @extends Roo.bootstrap.nav.Simplebar
22039  * @parent Roo.bootstrap.Popover
22040  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22041  * @licence LGPL
22042  * Bootstrap Popover header navigation class
22043  * FIXME? should this go under nav?
22044  *
22045  * 
22046  * @constructor
22047  * Create a new Popover Header Navigation 
22048  * @param {Object} config The config object
22049  */
22050
22051 Roo.bootstrap.PopoverNav = function(config){
22052     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22053 };
22054
22055 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22056     
22057     
22058     container_method : 'getPopoverHeader' 
22059     
22060      
22061     
22062     
22063    
22064 });
22065
22066  
22067
22068  /*
22069  * - LGPL
22070  *
22071  * Progress
22072  * 
22073  */
22074
22075 /**
22076  * @class Roo.bootstrap.Progress
22077  * @extends Roo.bootstrap.Component
22078  * @children Roo.bootstrap.ProgressBar
22079  * Bootstrap Progress class
22080  * @cfg {Boolean} striped striped of the progress bar
22081  * @cfg {Boolean} active animated of the progress bar
22082  * 
22083  * 
22084  * @constructor
22085  * Create a new Progress
22086  * @param {Object} config The config object
22087  */
22088
22089 Roo.bootstrap.Progress = function(config){
22090     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22091 };
22092
22093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22094     
22095     striped : false,
22096     active: false,
22097     
22098     getAutoCreate : function(){
22099         var cfg = {
22100             tag: 'div',
22101             cls: 'progress'
22102         };
22103         
22104         
22105         if(this.striped){
22106             cfg.cls += ' progress-striped';
22107         }
22108       
22109         if(this.active){
22110             cfg.cls += ' active';
22111         }
22112         
22113         
22114         return cfg;
22115     }
22116    
22117 });
22118
22119  
22120
22121  /*
22122  * - LGPL
22123  *
22124  * ProgressBar
22125  * 
22126  */
22127
22128 /**
22129  * @class Roo.bootstrap.ProgressBar
22130  * @extends Roo.bootstrap.Component
22131  * Bootstrap ProgressBar class
22132  * @cfg {Number} aria_valuenow aria-value now
22133  * @cfg {Number} aria_valuemin aria-value min
22134  * @cfg {Number} aria_valuemax aria-value max
22135  * @cfg {String} label label for the progress bar
22136  * @cfg {String} panel (success | info | warning | danger )
22137  * @cfg {String} role role of the progress bar
22138  * @cfg {String} sr_only text
22139  * 
22140  * 
22141  * @constructor
22142  * Create a new ProgressBar
22143  * @param {Object} config The config object
22144  */
22145
22146 Roo.bootstrap.ProgressBar = function(config){
22147     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22148 };
22149
22150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22151     
22152     aria_valuenow : 0,
22153     aria_valuemin : 0,
22154     aria_valuemax : 100,
22155     label : false,
22156     panel : false,
22157     role : false,
22158     sr_only: false,
22159     
22160     getAutoCreate : function()
22161     {
22162         
22163         var cfg = {
22164             tag: 'div',
22165             cls: 'progress-bar',
22166             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22167         };
22168         
22169         if(this.sr_only){
22170             cfg.cn = {
22171                 tag: 'span',
22172                 cls: 'sr-only',
22173                 html: this.sr_only
22174             }
22175         }
22176         
22177         if(this.role){
22178             cfg.role = this.role;
22179         }
22180         
22181         if(this.aria_valuenow){
22182             cfg['aria-valuenow'] = this.aria_valuenow;
22183         }
22184         
22185         if(this.aria_valuemin){
22186             cfg['aria-valuemin'] = this.aria_valuemin;
22187         }
22188         
22189         if(this.aria_valuemax){
22190             cfg['aria-valuemax'] = this.aria_valuemax;
22191         }
22192         
22193         if(this.label && !this.sr_only){
22194             cfg.html = this.label;
22195         }
22196         
22197         if(this.panel){
22198             cfg.cls += ' progress-bar-' + this.panel;
22199         }
22200         
22201         return cfg;
22202     },
22203     
22204     update : function(aria_valuenow)
22205     {
22206         this.aria_valuenow = aria_valuenow;
22207         
22208         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22209     }
22210    
22211 });
22212
22213  
22214
22215  /**
22216  * @class Roo.bootstrap.TabGroup
22217  * @extends Roo.bootstrap.Column
22218  * @children Roo.bootstrap.TabPanel
22219  * Bootstrap Column class
22220  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22221  * @cfg {Boolean} carousel true to make the group behave like a carousel
22222  * @cfg {Boolean} bullets show bullets for the panels
22223  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22224  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22225  * @cfg {Boolean} showarrow (true|false) show arrow default true
22226  * 
22227  * @constructor
22228  * Create a new TabGroup
22229  * @param {Object} config The config object
22230  */
22231
22232 Roo.bootstrap.TabGroup = function(config){
22233     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22234     if (!this.navId) {
22235         this.navId = Roo.id();
22236     }
22237     this.tabs = [];
22238     Roo.bootstrap.TabGroup.register(this);
22239     
22240 };
22241
22242 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22243     
22244     carousel : false,
22245     transition : false,
22246     bullets : 0,
22247     timer : 0,
22248     autoslide : false,
22249     slideFn : false,
22250     slideOnTouch : false,
22251     showarrow : true,
22252     
22253     getAutoCreate : function()
22254     {
22255         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22256         
22257         cfg.cls += ' tab-content';
22258         
22259         if (this.carousel) {
22260             cfg.cls += ' carousel slide';
22261             
22262             cfg.cn = [{
22263                cls : 'carousel-inner',
22264                cn : []
22265             }];
22266         
22267             if(this.bullets  && !Roo.isTouch){
22268                 
22269                 var bullets = {
22270                     cls : 'carousel-bullets',
22271                     cn : []
22272                 };
22273                
22274                 if(this.bullets_cls){
22275                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22276                 }
22277                 
22278                 bullets.cn.push({
22279                     cls : 'clear'
22280                 });
22281                 
22282                 cfg.cn[0].cn.push(bullets);
22283             }
22284             
22285             if(this.showarrow){
22286                 cfg.cn[0].cn.push({
22287                     tag : 'div',
22288                     class : 'carousel-arrow',
22289                     cn : [
22290                         {
22291                             tag : 'div',
22292                             class : 'carousel-prev',
22293                             cn : [
22294                                 {
22295                                     tag : 'i',
22296                                     class : 'fa fa-chevron-left'
22297                                 }
22298                             ]
22299                         },
22300                         {
22301                             tag : 'div',
22302                             class : 'carousel-next',
22303                             cn : [
22304                                 {
22305                                     tag : 'i',
22306                                     class : 'fa fa-chevron-right'
22307                                 }
22308                             ]
22309                         }
22310                     ]
22311                 });
22312             }
22313             
22314         }
22315         
22316         return cfg;
22317     },
22318     
22319     initEvents:  function()
22320     {
22321 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22322 //            this.el.on("touchstart", this.onTouchStart, this);
22323 //        }
22324         
22325         if(this.autoslide){
22326             var _this = this;
22327             
22328             this.slideFn = window.setInterval(function() {
22329                 _this.showPanelNext();
22330             }, this.timer);
22331         }
22332         
22333         if(this.showarrow){
22334             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22335             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22336         }
22337         
22338         
22339     },
22340     
22341 //    onTouchStart : function(e, el, o)
22342 //    {
22343 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22344 //            return;
22345 //        }
22346 //        
22347 //        this.showPanelNext();
22348 //    },
22349     
22350     
22351     getChildContainer : function()
22352     {
22353         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22354     },
22355     
22356     /**
22357     * register a Navigation item
22358     * @param {Roo.bootstrap.nav.Item} the navitem to add
22359     */
22360     register : function(item)
22361     {
22362         this.tabs.push( item);
22363         item.navId = this.navId; // not really needed..
22364         this.addBullet();
22365     
22366     },
22367     
22368     getActivePanel : function()
22369     {
22370         var r = false;
22371         Roo.each(this.tabs, function(t) {
22372             if (t.active) {
22373                 r = t;
22374                 return false;
22375             }
22376             return null;
22377         });
22378         return r;
22379         
22380     },
22381     getPanelByName : function(n)
22382     {
22383         var r = false;
22384         Roo.each(this.tabs, function(t) {
22385             if (t.tabId == n) {
22386                 r = t;
22387                 return false;
22388             }
22389             return null;
22390         });
22391         return r;
22392     },
22393     indexOfPanel : function(p)
22394     {
22395         var r = false;
22396         Roo.each(this.tabs, function(t,i) {
22397             if (t.tabId == p.tabId) {
22398                 r = i;
22399                 return false;
22400             }
22401             return null;
22402         });
22403         return r;
22404     },
22405     /**
22406      * show a specific panel
22407      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22408      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22409      */
22410     showPanel : function (pan)
22411     {
22412         if(this.transition || typeof(pan) == 'undefined'){
22413             Roo.log("waiting for the transitionend");
22414             return false;
22415         }
22416         
22417         if (typeof(pan) == 'number') {
22418             pan = this.tabs[pan];
22419         }
22420         
22421         if (typeof(pan) == 'string') {
22422             pan = this.getPanelByName(pan);
22423         }
22424         
22425         var cur = this.getActivePanel();
22426         
22427         if(!pan || !cur){
22428             Roo.log('pan or acitve pan is undefined');
22429             return false;
22430         }
22431         
22432         if (pan.tabId == this.getActivePanel().tabId) {
22433             return true;
22434         }
22435         
22436         if (false === cur.fireEvent('beforedeactivate')) {
22437             return false;
22438         }
22439         
22440         if(this.bullets > 0 && !Roo.isTouch){
22441             this.setActiveBullet(this.indexOfPanel(pan));
22442         }
22443         
22444         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22445             
22446             //class="carousel-item carousel-item-next carousel-item-left"
22447             
22448             this.transition = true;
22449             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22450             var lr = dir == 'next' ? 'left' : 'right';
22451             pan.el.addClass(dir); // or prev
22452             pan.el.addClass('carousel-item-' + dir); // or prev
22453             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22454             cur.el.addClass(lr); // or right
22455             pan.el.addClass(lr);
22456             cur.el.addClass('carousel-item-' +lr); // or right
22457             pan.el.addClass('carousel-item-' +lr);
22458             
22459             
22460             var _this = this;
22461             cur.el.on('transitionend', function() {
22462                 Roo.log("trans end?");
22463                 
22464                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22465                 pan.setActive(true);
22466                 
22467                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22468                 cur.setActive(false);
22469                 
22470                 _this.transition = false;
22471                 
22472             }, this, { single:  true } );
22473             
22474             return true;
22475         }
22476         
22477         cur.setActive(false);
22478         pan.setActive(true);
22479         
22480         return true;
22481         
22482     },
22483     showPanelNext : function()
22484     {
22485         var i = this.indexOfPanel(this.getActivePanel());
22486         
22487         if (i >= this.tabs.length - 1 && !this.autoslide) {
22488             return;
22489         }
22490         
22491         if (i >= this.tabs.length - 1 && this.autoslide) {
22492             i = -1;
22493         }
22494         
22495         this.showPanel(this.tabs[i+1]);
22496     },
22497     
22498     showPanelPrev : function()
22499     {
22500         var i = this.indexOfPanel(this.getActivePanel());
22501         
22502         if (i  < 1 && !this.autoslide) {
22503             return;
22504         }
22505         
22506         if (i < 1 && this.autoslide) {
22507             i = this.tabs.length;
22508         }
22509         
22510         this.showPanel(this.tabs[i-1]);
22511     },
22512     
22513     
22514     addBullet: function()
22515     {
22516         if(!this.bullets || Roo.isTouch){
22517             return;
22518         }
22519         var ctr = this.el.select('.carousel-bullets',true).first();
22520         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22521         var bullet = ctr.createChild({
22522             cls : 'bullet bullet-' + i
22523         },ctr.dom.lastChild);
22524         
22525         
22526         var _this = this;
22527         
22528         bullet.on('click', (function(e, el, o, ii, t){
22529
22530             e.preventDefault();
22531
22532             this.showPanel(ii);
22533
22534             if(this.autoslide && this.slideFn){
22535                 clearInterval(this.slideFn);
22536                 this.slideFn = window.setInterval(function() {
22537                     _this.showPanelNext();
22538                 }, this.timer);
22539             }
22540
22541         }).createDelegate(this, [i, bullet], true));
22542                 
22543         
22544     },
22545      
22546     setActiveBullet : function(i)
22547     {
22548         if(Roo.isTouch){
22549             return;
22550         }
22551         
22552         Roo.each(this.el.select('.bullet', true).elements, function(el){
22553             el.removeClass('selected');
22554         });
22555
22556         var bullet = this.el.select('.bullet-' + i, true).first();
22557         
22558         if(!bullet){
22559             return;
22560         }
22561         
22562         bullet.addClass('selected');
22563     }
22564     
22565     
22566   
22567 });
22568
22569  
22570
22571  
22572  
22573 Roo.apply(Roo.bootstrap.TabGroup, {
22574     
22575     groups: {},
22576      /**
22577     * register a Navigation Group
22578     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22579     */
22580     register : function(navgrp)
22581     {
22582         this.groups[navgrp.navId] = navgrp;
22583         
22584     },
22585     /**
22586     * fetch a Navigation Group based on the navigation ID
22587     * if one does not exist , it will get created.
22588     * @param {string} the navgroup to add
22589     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22590     */
22591     get: function(navId) {
22592         if (typeof(this.groups[navId]) == 'undefined') {
22593             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22594         }
22595         return this.groups[navId] ;
22596     }
22597     
22598     
22599     
22600 });
22601
22602  /*
22603  * - LGPL
22604  *
22605  * TabPanel
22606  * 
22607  */
22608
22609 /**
22610  * @class Roo.bootstrap.TabPanel
22611  * @extends Roo.bootstrap.Component
22612  * @children Roo.bootstrap.Component
22613  * Bootstrap TabPanel class
22614  * @cfg {Boolean} active panel active
22615  * @cfg {String} html panel content
22616  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22617  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22618  * @cfg {String} href click to link..
22619  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22620  * 
22621  * 
22622  * @constructor
22623  * Create a new TabPanel
22624  * @param {Object} config The config object
22625  */
22626
22627 Roo.bootstrap.TabPanel = function(config){
22628     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22629     this.addEvents({
22630         /**
22631              * @event changed
22632              * Fires when the active status changes
22633              * @param {Roo.bootstrap.TabPanel} this
22634              * @param {Boolean} state the new state
22635             
22636          */
22637         'changed': true,
22638         /**
22639              * @event beforedeactivate
22640              * Fires before a tab is de-activated - can be used to do validation on a form.
22641              * @param {Roo.bootstrap.TabPanel} this
22642              * @return {Boolean} false if there is an error
22643             
22644          */
22645         'beforedeactivate': true
22646      });
22647     
22648     this.tabId = this.tabId || Roo.id();
22649   
22650 };
22651
22652 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22653     
22654     active: false,
22655     html: false,
22656     tabId: false,
22657     navId : false,
22658     href : '',
22659     touchSlide : false,
22660     getAutoCreate : function(){
22661         
22662         
22663         var cfg = {
22664             tag: 'div',
22665             // item is needed for carousel - not sure if it has any effect otherwise
22666             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22667             html: this.html || ''
22668         };
22669         
22670         if(this.active){
22671             cfg.cls += ' active';
22672         }
22673         
22674         if(this.tabId){
22675             cfg.tabId = this.tabId;
22676         }
22677         
22678         
22679         
22680         return cfg;
22681     },
22682     
22683     initEvents:  function()
22684     {
22685         var p = this.parent();
22686         
22687         this.navId = this.navId || p.navId;
22688         
22689         if (typeof(this.navId) != 'undefined') {
22690             // not really needed.. but just in case.. parent should be a NavGroup.
22691             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22692             
22693             tg.register(this);
22694             
22695             var i = tg.tabs.length - 1;
22696             
22697             if(this.active && tg.bullets > 0 && i < tg.bullets){
22698                 tg.setActiveBullet(i);
22699             }
22700         }
22701         
22702         this.el.on('click', this.onClick, this);
22703         
22704         if(Roo.isTouch && this.touchSlide){
22705             this.el.on("touchstart", this.onTouchStart, this);
22706             this.el.on("touchmove", this.onTouchMove, this);
22707             this.el.on("touchend", this.onTouchEnd, this);
22708         }
22709         
22710     },
22711     
22712     onRender : function(ct, position)
22713     {
22714         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22715     },
22716     
22717     setActive : function(state)
22718     {
22719         Roo.log("panel - set active " + this.tabId + "=" + state);
22720         
22721         this.active = state;
22722         if (!state) {
22723             this.el.removeClass('active');
22724             
22725         } else  if (!this.el.hasClass('active')) {
22726             this.el.addClass('active');
22727         }
22728         
22729         this.fireEvent('changed', this, state);
22730     },
22731     
22732     onClick : function(e)
22733     {
22734         e.preventDefault();
22735         
22736         if(!this.href.length){
22737             return;
22738         }
22739         
22740         window.location.href = this.href;
22741     },
22742     
22743     startX : 0,
22744     startY : 0,
22745     endX : 0,
22746     endY : 0,
22747     swiping : false,
22748     
22749     onTouchStart : function(e)
22750     {
22751         this.swiping = false;
22752         
22753         this.startX = e.browserEvent.touches[0].clientX;
22754         this.startY = e.browserEvent.touches[0].clientY;
22755     },
22756     
22757     onTouchMove : function(e)
22758     {
22759         this.swiping = true;
22760         
22761         this.endX = e.browserEvent.touches[0].clientX;
22762         this.endY = e.browserEvent.touches[0].clientY;
22763     },
22764     
22765     onTouchEnd : function(e)
22766     {
22767         if(!this.swiping){
22768             this.onClick(e);
22769             return;
22770         }
22771         
22772         var tabGroup = this.parent();
22773         
22774         if(this.endX > this.startX){ // swiping right
22775             tabGroup.showPanelPrev();
22776             return;
22777         }
22778         
22779         if(this.startX > this.endX){ // swiping left
22780             tabGroup.showPanelNext();
22781             return;
22782         }
22783     }
22784     
22785     
22786 });
22787  
22788
22789  
22790
22791  /*
22792  * - LGPL
22793  *
22794  * DateField
22795  * 
22796  */
22797
22798 /**
22799  * @class Roo.bootstrap.form.DateField
22800  * @extends Roo.bootstrap.form.Input
22801  * Bootstrap DateField class
22802  * @cfg {Number} weekStart default 0
22803  * @cfg {String} viewMode default empty, (months|years)
22804  * @cfg {String} minViewMode default empty, (months|years)
22805  * @cfg {Number} startDate default -Infinity
22806  * @cfg {Number} endDate default Infinity
22807  * @cfg {Boolean} todayHighlight default false
22808  * @cfg {Boolean} todayBtn default false
22809  * @cfg {Boolean} calendarWeeks default false
22810  * @cfg {Object} daysOfWeekDisabled default empty
22811  * @cfg {Boolean} singleMode default false (true | false)
22812  * 
22813  * @cfg {Boolean} keyboardNavigation default true
22814  * @cfg {String} language default en
22815  * 
22816  * @constructor
22817  * Create a new DateField
22818  * @param {Object} config The config object
22819  */
22820
22821 Roo.bootstrap.form.DateField = function(config){
22822     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22823      this.addEvents({
22824             /**
22825              * @event show
22826              * Fires when this field show.
22827              * @param {Roo.bootstrap.form.DateField} this
22828              * @param {Mixed} date The date value
22829              */
22830             show : true,
22831             /**
22832              * @event show
22833              * Fires when this field hide.
22834              * @param {Roo.bootstrap.form.DateField} this
22835              * @param {Mixed} date The date value
22836              */
22837             hide : true,
22838             /**
22839              * @event select
22840              * Fires when select a date.
22841              * @param {Roo.bootstrap.form.DateField} this
22842              * @param {Mixed} date The date value
22843              */
22844             select : true,
22845             /**
22846              * @event beforeselect
22847              * Fires when before select a date.
22848              * @param {Roo.bootstrap.form.DateField} this
22849              * @param {Mixed} date The date value
22850              */
22851             beforeselect : true
22852         });
22853 };
22854
22855 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22856     
22857     /**
22858      * @cfg {String} format
22859      * The default date format string which can be overriden for localization support.  The format must be
22860      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22861      */
22862     format : "m/d/y",
22863     /**
22864      * @cfg {String} altFormats
22865      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22866      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22867      */
22868     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22869     
22870     weekStart : 0,
22871     
22872     viewMode : '',
22873     
22874     minViewMode : '',
22875     
22876     todayHighlight : false,
22877     
22878     todayBtn: false,
22879     
22880     language: 'en',
22881     
22882     keyboardNavigation: true,
22883     
22884     calendarWeeks: false,
22885     
22886     startDate: -Infinity,
22887     
22888     endDate: Infinity,
22889     
22890     daysOfWeekDisabled: [],
22891     
22892     _events: [],
22893     
22894     singleMode : false,
22895     
22896     UTCDate: function()
22897     {
22898         return new Date(Date.UTC.apply(Date, arguments));
22899     },
22900     
22901     UTCToday: function()
22902     {
22903         var today = new Date();
22904         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22905     },
22906     
22907     getDate: function() {
22908             var d = this.getUTCDate();
22909             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22910     },
22911     
22912     getUTCDate: function() {
22913             return this.date;
22914     },
22915     
22916     setDate: function(d) {
22917             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22918     },
22919     
22920     setUTCDate: function(d) {
22921             this.date = d;
22922             this.setValue(this.formatDate(this.date));
22923     },
22924         
22925     onRender: function(ct, position)
22926     {
22927         
22928         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22929         
22930         this.language = this.language || 'en';
22931         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22932         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22933         
22934         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22935         this.format = this.format || 'm/d/y';
22936         this.isInline = false;
22937         this.isInput = true;
22938         this.component = this.el.select('.add-on', true).first() || false;
22939         this.component = (this.component && this.component.length === 0) ? false : this.component;
22940         this.hasInput = this.component && this.inputEl().length;
22941         
22942         if (typeof(this.minViewMode === 'string')) {
22943             switch (this.minViewMode) {
22944                 case 'months':
22945                     this.minViewMode = 1;
22946                     break;
22947                 case 'years':
22948                     this.minViewMode = 2;
22949                     break;
22950                 default:
22951                     this.minViewMode = 0;
22952                     break;
22953             }
22954         }
22955         
22956         if (typeof(this.viewMode === 'string')) {
22957             switch (this.viewMode) {
22958                 case 'months':
22959                     this.viewMode = 1;
22960                     break;
22961                 case 'years':
22962                     this.viewMode = 2;
22963                     break;
22964                 default:
22965                     this.viewMode = 0;
22966                     break;
22967             }
22968         }
22969                 
22970         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22971         
22972 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22973         
22974         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22975         
22976         this.picker().on('mousedown', this.onMousedown, this);
22977         this.picker().on('click', this.onClick, this);
22978         
22979         this.picker().addClass('datepicker-dropdown');
22980         
22981         this.startViewMode = this.viewMode;
22982         
22983         if(this.singleMode){
22984             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22985                 v.setVisibilityMode(Roo.Element.DISPLAY);
22986                 v.hide();
22987             });
22988             
22989             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22990                 v.setStyle('width', '189px');
22991             });
22992         }
22993         
22994         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22995             if(!this.calendarWeeks){
22996                 v.remove();
22997                 return;
22998             }
22999             
23000             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23001             v.attr('colspan', function(i, val){
23002                 return parseInt(val) + 1;
23003             });
23004         });
23005                         
23006         
23007         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23008         
23009         this.setStartDate(this.startDate);
23010         this.setEndDate(this.endDate);
23011         
23012         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23013         
23014         this.fillDow();
23015         this.fillMonths();
23016         this.update();
23017         this.showMode();
23018         
23019         if(this.isInline) {
23020             this.showPopup();
23021         }
23022     },
23023     
23024     picker : function()
23025     {
23026         return this.pickerEl;
23027 //        return this.el.select('.datepicker', true).first();
23028     },
23029     
23030     fillDow: function()
23031     {
23032         var dowCnt = this.weekStart;
23033         
23034         var dow = {
23035             tag: 'tr',
23036             cn: [
23037                 
23038             ]
23039         };
23040         
23041         if(this.calendarWeeks){
23042             dow.cn.push({
23043                 tag: 'th',
23044                 cls: 'cw',
23045                 html: '&nbsp;'
23046             })
23047         }
23048         
23049         while (dowCnt < this.weekStart + 7) {
23050             dow.cn.push({
23051                 tag: 'th',
23052                 cls: 'dow',
23053                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23054             });
23055         }
23056         
23057         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23058     },
23059     
23060     fillMonths: function()
23061     {    
23062         var i = 0;
23063         var months = this.picker().select('>.datepicker-months td', true).first();
23064         
23065         months.dom.innerHTML = '';
23066         
23067         while (i < 12) {
23068             var month = {
23069                 tag: 'span',
23070                 cls: 'month',
23071                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23072             };
23073             
23074             months.createChild(month);
23075         }
23076         
23077     },
23078     
23079     update: function()
23080     {
23081         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;
23082         
23083         if (this.date < this.startDate) {
23084             this.viewDate = new Date(this.startDate);
23085         } else if (this.date > this.endDate) {
23086             this.viewDate = new Date(this.endDate);
23087         } else {
23088             this.viewDate = new Date(this.date);
23089         }
23090         
23091         this.fill();
23092     },
23093     
23094     fill: function() 
23095     {
23096         var d = new Date(this.viewDate),
23097                 year = d.getUTCFullYear(),
23098                 month = d.getUTCMonth(),
23099                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23100                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23101                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23102                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23103                 currentDate = this.date && this.date.valueOf(),
23104                 today = this.UTCToday();
23105         
23106         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23107         
23108 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23109         
23110 //        this.picker.select('>tfoot th.today').
23111 //                                              .text(dates[this.language].today)
23112 //                                              .toggle(this.todayBtn !== false);
23113     
23114         this.updateNavArrows();
23115         this.fillMonths();
23116                                                 
23117         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23118         
23119         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23120          
23121         prevMonth.setUTCDate(day);
23122         
23123         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23124         
23125         var nextMonth = new Date(prevMonth);
23126         
23127         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23128         
23129         nextMonth = nextMonth.valueOf();
23130         
23131         var fillMonths = false;
23132         
23133         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23134         
23135         while(prevMonth.valueOf() <= nextMonth) {
23136             var clsName = '';
23137             
23138             if (prevMonth.getUTCDay() === this.weekStart) {
23139                 if(fillMonths){
23140                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23141                 }
23142                     
23143                 fillMonths = {
23144                     tag: 'tr',
23145                     cn: []
23146                 };
23147                 
23148                 if(this.calendarWeeks){
23149                     // ISO 8601: First week contains first thursday.
23150                     // ISO also states week starts on Monday, but we can be more abstract here.
23151                     var
23152                     // Start of current week: based on weekstart/current date
23153                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23154                     // Thursday of this week
23155                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23156                     // First Thursday of year, year from thursday
23157                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23158                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23159                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23160                     
23161                     fillMonths.cn.push({
23162                         tag: 'td',
23163                         cls: 'cw',
23164                         html: calWeek
23165                     });
23166                 }
23167             }
23168             
23169             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23170                 clsName += ' old';
23171             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23172                 clsName += ' new';
23173             }
23174             if (this.todayHighlight &&
23175                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23176                 prevMonth.getUTCMonth() == today.getMonth() &&
23177                 prevMonth.getUTCDate() == today.getDate()) {
23178                 clsName += ' today';
23179             }
23180             
23181             if (currentDate && prevMonth.valueOf() === currentDate) {
23182                 clsName += ' active';
23183             }
23184             
23185             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23186                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23187                     clsName += ' disabled';
23188             }
23189             
23190             fillMonths.cn.push({
23191                 tag: 'td',
23192                 cls: 'day ' + clsName,
23193                 html: prevMonth.getDate()
23194             });
23195             
23196             prevMonth.setDate(prevMonth.getDate()+1);
23197         }
23198           
23199         var currentYear = this.date && this.date.getUTCFullYear();
23200         var currentMonth = this.date && this.date.getUTCMonth();
23201         
23202         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23203         
23204         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23205             v.removeClass('active');
23206             
23207             if(currentYear === year && k === currentMonth){
23208                 v.addClass('active');
23209             }
23210             
23211             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23212                 v.addClass('disabled');
23213             }
23214             
23215         });
23216         
23217         
23218         year = parseInt(year/10, 10) * 10;
23219         
23220         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23221         
23222         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23223         
23224         year -= 1;
23225         for (var i = -1; i < 11; i++) {
23226             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23227                 tag: 'span',
23228                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23229                 html: year
23230             });
23231             
23232             year += 1;
23233         }
23234     },
23235     
23236     showMode: function(dir) 
23237     {
23238         if (dir) {
23239             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23240         }
23241         
23242         Roo.each(this.picker().select('>div',true).elements, function(v){
23243             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23244             v.hide();
23245         });
23246         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23247     },
23248     
23249     place: function()
23250     {
23251         if(this.isInline) {
23252             return;
23253         }
23254         
23255         this.picker().removeClass(['bottom', 'top']);
23256         
23257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23258             /*
23259              * place to the top of element!
23260              *
23261              */
23262             
23263             this.picker().addClass('top');
23264             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23265             
23266             return;
23267         }
23268         
23269         this.picker().addClass('bottom');
23270         
23271         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23272     },
23273     
23274     parseDate : function(value)
23275     {
23276         if(!value || value instanceof Date){
23277             return value;
23278         }
23279         var v = Date.parseDate(value, this.format);
23280         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23281             v = Date.parseDate(value, 'Y-m-d');
23282         }
23283         if(!v && this.altFormats){
23284             if(!this.altFormatsArray){
23285                 this.altFormatsArray = this.altFormats.split("|");
23286             }
23287             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23288                 v = Date.parseDate(value, this.altFormatsArray[i]);
23289             }
23290         }
23291         return v;
23292     },
23293     
23294     formatDate : function(date, fmt)
23295     {   
23296         return (!date || !(date instanceof Date)) ?
23297         date : date.dateFormat(fmt || this.format);
23298     },
23299     
23300     onFocus : function()
23301     {
23302         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23303         this.showPopup();
23304     },
23305     
23306     onBlur : function()
23307     {
23308         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23309         
23310         var d = this.inputEl().getValue();
23311         
23312         this.setValue(d);
23313                 
23314         this.hidePopup();
23315     },
23316     
23317     showPopup : function()
23318     {
23319         this.picker().show();
23320         this.update();
23321         this.place();
23322         
23323         this.fireEvent('showpopup', this, this.date);
23324     },
23325     
23326     hidePopup : function()
23327     {
23328         if(this.isInline) {
23329             return;
23330         }
23331         this.picker().hide();
23332         this.viewMode = this.startViewMode;
23333         this.showMode();
23334         
23335         this.fireEvent('hidepopup', this, this.date);
23336         
23337     },
23338     
23339     onMousedown: function(e)
23340     {
23341         e.stopPropagation();
23342         e.preventDefault();
23343     },
23344     
23345     keyup: function(e)
23346     {
23347         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23348         this.update();
23349     },
23350
23351     setValue: function(v)
23352     {
23353         if(this.fireEvent('beforeselect', this, v) !== false){
23354             var d = new Date(this.parseDate(v) ).clearTime();
23355         
23356             if(isNaN(d.getTime())){
23357                 this.date = this.viewDate = '';
23358                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23359                 return;
23360             }
23361
23362             v = this.formatDate(d);
23363
23364             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23365
23366             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23367
23368             this.update();
23369
23370             this.fireEvent('select', this, this.date);
23371         }
23372     },
23373     
23374     getValue: function()
23375     {
23376         return this.formatDate(this.date);
23377     },
23378     
23379     fireKey: function(e)
23380     {
23381         if (!this.picker().isVisible()){
23382             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23383                 this.showPopup();
23384             }
23385             return;
23386         }
23387         
23388         var dateChanged = false,
23389         dir, day, month,
23390         newDate, newViewDate;
23391         
23392         switch(e.keyCode){
23393             case 27: // escape
23394                 this.hidePopup();
23395                 e.preventDefault();
23396                 break;
23397             case 37: // left
23398             case 39: // right
23399                 if (!this.keyboardNavigation) {
23400                     break;
23401                 }
23402                 dir = e.keyCode == 37 ? -1 : 1;
23403                 
23404                 if (e.ctrlKey){
23405                     newDate = this.moveYear(this.date, dir);
23406                     newViewDate = this.moveYear(this.viewDate, dir);
23407                 } else if (e.shiftKey){
23408                     newDate = this.moveMonth(this.date, dir);
23409                     newViewDate = this.moveMonth(this.viewDate, dir);
23410                 } else {
23411                     newDate = new Date(this.date);
23412                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23413                     newViewDate = new Date(this.viewDate);
23414                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23415                 }
23416                 if (this.dateWithinRange(newDate)){
23417                     this.date = newDate;
23418                     this.viewDate = newViewDate;
23419                     this.setValue(this.formatDate(this.date));
23420 //                    this.update();
23421                     e.preventDefault();
23422                     dateChanged = true;
23423                 }
23424                 break;
23425             case 38: // up
23426             case 40: // down
23427                 if (!this.keyboardNavigation) {
23428                     break;
23429                 }
23430                 dir = e.keyCode == 38 ? -1 : 1;
23431                 if (e.ctrlKey){
23432                     newDate = this.moveYear(this.date, dir);
23433                     newViewDate = this.moveYear(this.viewDate, dir);
23434                 } else if (e.shiftKey){
23435                     newDate = this.moveMonth(this.date, dir);
23436                     newViewDate = this.moveMonth(this.viewDate, dir);
23437                 } else {
23438                     newDate = new Date(this.date);
23439                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23440                     newViewDate = new Date(this.viewDate);
23441                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23442                 }
23443                 if (this.dateWithinRange(newDate)){
23444                     this.date = newDate;
23445                     this.viewDate = newViewDate;
23446                     this.setValue(this.formatDate(this.date));
23447 //                    this.update();
23448                     e.preventDefault();
23449                     dateChanged = true;
23450                 }
23451                 break;
23452             case 13: // enter
23453                 this.setValue(this.formatDate(this.date));
23454                 this.hidePopup();
23455                 e.preventDefault();
23456                 break;
23457             case 9: // tab
23458                 this.setValue(this.formatDate(this.date));
23459                 this.hidePopup();
23460                 break;
23461             case 16: // shift
23462             case 17: // ctrl
23463             case 18: // alt
23464                 break;
23465             default :
23466                 this.hidePopup();
23467                 
23468         }
23469     },
23470     
23471     
23472     onClick: function(e) 
23473     {
23474         e.stopPropagation();
23475         e.preventDefault();
23476         
23477         var target = e.getTarget();
23478         
23479         if(target.nodeName.toLowerCase() === 'i'){
23480             target = Roo.get(target).dom.parentNode;
23481         }
23482         
23483         var nodeName = target.nodeName;
23484         var className = target.className;
23485         var html = target.innerHTML;
23486         //Roo.log(nodeName);
23487         
23488         switch(nodeName.toLowerCase()) {
23489             case 'th':
23490                 switch(className) {
23491                     case 'switch':
23492                         this.showMode(1);
23493                         break;
23494                     case 'prev':
23495                     case 'next':
23496                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23497                         switch(this.viewMode){
23498                                 case 0:
23499                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23500                                         break;
23501                                 case 1:
23502                                 case 2:
23503                                         this.viewDate = this.moveYear(this.viewDate, dir);
23504                                         break;
23505                         }
23506                         this.fill();
23507                         break;
23508                     case 'today':
23509                         var date = new Date();
23510                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23511 //                        this.fill()
23512                         this.setValue(this.formatDate(this.date));
23513                         
23514                         this.hidePopup();
23515                         break;
23516                 }
23517                 break;
23518             case 'span':
23519                 if (className.indexOf('disabled') < 0) {
23520                 if (!this.viewDate) {
23521                     this.viewDate = new Date();
23522                 }
23523                 this.viewDate.setUTCDate(1);
23524                     if (className.indexOf('month') > -1) {
23525                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23526                     } else {
23527                         var year = parseInt(html, 10) || 0;
23528                         this.viewDate.setUTCFullYear(year);
23529                         
23530                     }
23531                     
23532                     if(this.singleMode){
23533                         this.setValue(this.formatDate(this.viewDate));
23534                         this.hidePopup();
23535                         return;
23536                     }
23537                     
23538                     this.showMode(-1);
23539                     this.fill();
23540                 }
23541                 break;
23542                 
23543             case 'td':
23544                 //Roo.log(className);
23545                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23546                     var day = parseInt(html, 10) || 1;
23547                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23548                         month = (this.viewDate || new Date()).getUTCMonth();
23549
23550                     if (className.indexOf('old') > -1) {
23551                         if(month === 0 ){
23552                             month = 11;
23553                             year -= 1;
23554                         }else{
23555                             month -= 1;
23556                         }
23557                     } else if (className.indexOf('new') > -1) {
23558                         if (month == 11) {
23559                             month = 0;
23560                             year += 1;
23561                         } else {
23562                             month += 1;
23563                         }
23564                     }
23565                     //Roo.log([year,month,day]);
23566                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23567                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23568 //                    this.fill();
23569                     //Roo.log(this.formatDate(this.date));
23570                     this.setValue(this.formatDate(this.date));
23571                     this.hidePopup();
23572                 }
23573                 break;
23574         }
23575     },
23576     
23577     setStartDate: function(startDate)
23578     {
23579         this.startDate = startDate || -Infinity;
23580         if (this.startDate !== -Infinity) {
23581             this.startDate = this.parseDate(this.startDate);
23582         }
23583         this.update();
23584         this.updateNavArrows();
23585     },
23586
23587     setEndDate: function(endDate)
23588     {
23589         this.endDate = endDate || Infinity;
23590         if (this.endDate !== Infinity) {
23591             this.endDate = this.parseDate(this.endDate);
23592         }
23593         this.update();
23594         this.updateNavArrows();
23595     },
23596     
23597     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23598     {
23599         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23600         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23601             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23602         }
23603         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23604             return parseInt(d, 10);
23605         });
23606         this.update();
23607         this.updateNavArrows();
23608     },
23609     
23610     updateNavArrows: function() 
23611     {
23612         if(this.singleMode){
23613             return;
23614         }
23615         
23616         var d = new Date(this.viewDate),
23617         year = d.getUTCFullYear(),
23618         month = d.getUTCMonth();
23619         
23620         Roo.each(this.picker().select('.prev', true).elements, function(v){
23621             v.show();
23622             switch (this.viewMode) {
23623                 case 0:
23624
23625                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23626                         v.hide();
23627                     }
23628                     break;
23629                 case 1:
23630                 case 2:
23631                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23632                         v.hide();
23633                     }
23634                     break;
23635             }
23636         });
23637         
23638         Roo.each(this.picker().select('.next', true).elements, function(v){
23639             v.show();
23640             switch (this.viewMode) {
23641                 case 0:
23642
23643                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23644                         v.hide();
23645                     }
23646                     break;
23647                 case 1:
23648                 case 2:
23649                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23650                         v.hide();
23651                     }
23652                     break;
23653             }
23654         })
23655     },
23656     
23657     moveMonth: function(date, dir)
23658     {
23659         if (!dir) {
23660             return date;
23661         }
23662         var new_date = new Date(date.valueOf()),
23663         day = new_date.getUTCDate(),
23664         month = new_date.getUTCMonth(),
23665         mag = Math.abs(dir),
23666         new_month, test;
23667         dir = dir > 0 ? 1 : -1;
23668         if (mag == 1){
23669             test = dir == -1
23670             // If going back one month, make sure month is not current month
23671             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23672             ? function(){
23673                 return new_date.getUTCMonth() == month;
23674             }
23675             // If going forward one month, make sure month is as expected
23676             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23677             : function(){
23678                 return new_date.getUTCMonth() != new_month;
23679             };
23680             new_month = month + dir;
23681             new_date.setUTCMonth(new_month);
23682             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23683             if (new_month < 0 || new_month > 11) {
23684                 new_month = (new_month + 12) % 12;
23685             }
23686         } else {
23687             // For magnitudes >1, move one month at a time...
23688             for (var i=0; i<mag; i++) {
23689                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23690                 new_date = this.moveMonth(new_date, dir);
23691             }
23692             // ...then reset the day, keeping it in the new month
23693             new_month = new_date.getUTCMonth();
23694             new_date.setUTCDate(day);
23695             test = function(){
23696                 return new_month != new_date.getUTCMonth();
23697             };
23698         }
23699         // Common date-resetting loop -- if date is beyond end of month, make it
23700         // end of month
23701         while (test()){
23702             new_date.setUTCDate(--day);
23703             new_date.setUTCMonth(new_month);
23704         }
23705         return new_date;
23706     },
23707
23708     moveYear: function(date, dir)
23709     {
23710         return this.moveMonth(date, dir*12);
23711     },
23712
23713     dateWithinRange: function(date)
23714     {
23715         return date >= this.startDate && date <= this.endDate;
23716     },
23717
23718     
23719     remove: function() 
23720     {
23721         this.picker().remove();
23722     },
23723     
23724     validateValue : function(value)
23725     {
23726         if(this.getVisibilityEl().hasClass('hidden')){
23727             return true;
23728         }
23729         
23730         if(value.length < 1)  {
23731             if(this.allowBlank){
23732                 return true;
23733             }
23734             return false;
23735         }
23736         
23737         if(value.length < this.minLength){
23738             return false;
23739         }
23740         if(value.length > this.maxLength){
23741             return false;
23742         }
23743         if(this.vtype){
23744             var vt = Roo.form.VTypes;
23745             if(!vt[this.vtype](value, this)){
23746                 return false;
23747             }
23748         }
23749         if(typeof this.validator == "function"){
23750             var msg = this.validator(value);
23751             if(msg !== true){
23752                 return false;
23753             }
23754         }
23755         
23756         if(this.regex && !this.regex.test(value)){
23757             return false;
23758         }
23759         
23760         if(typeof(this.parseDate(value)) == 'undefined'){
23761             return false;
23762         }
23763         
23764         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23765             return false;
23766         }      
23767         
23768         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23769             return false;
23770         } 
23771         
23772         
23773         return true;
23774     },
23775     
23776     reset : function()
23777     {
23778         this.date = this.viewDate = '';
23779         
23780         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23781     }
23782    
23783 });
23784
23785 Roo.apply(Roo.bootstrap.form.DateField,  {
23786     
23787     head : {
23788         tag: 'thead',
23789         cn: [
23790         {
23791             tag: 'tr',
23792             cn: [
23793             {
23794                 tag: 'th',
23795                 cls: 'prev',
23796                 html: '<i class="fa fa-arrow-left"/>'
23797             },
23798             {
23799                 tag: 'th',
23800                 cls: 'switch',
23801                 colspan: '5'
23802             },
23803             {
23804                 tag: 'th',
23805                 cls: 'next',
23806                 html: '<i class="fa fa-arrow-right"/>'
23807             }
23808
23809             ]
23810         }
23811         ]
23812     },
23813     
23814     content : {
23815         tag: 'tbody',
23816         cn: [
23817         {
23818             tag: 'tr',
23819             cn: [
23820             {
23821                 tag: 'td',
23822                 colspan: '7'
23823             }
23824             ]
23825         }
23826         ]
23827     },
23828     
23829     footer : {
23830         tag: 'tfoot',
23831         cn: [
23832         {
23833             tag: 'tr',
23834             cn: [
23835             {
23836                 tag: 'th',
23837                 colspan: '7',
23838                 cls: 'today'
23839             }
23840                     
23841             ]
23842         }
23843         ]
23844     },
23845     
23846     dates:{
23847         en: {
23848             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23849             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23850             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23853             today: "Today"
23854         }
23855     },
23856     
23857     modes: [
23858     {
23859         clsName: 'days',
23860         navFnc: 'Month',
23861         navStep: 1
23862     },
23863     {
23864         clsName: 'months',
23865         navFnc: 'FullYear',
23866         navStep: 1
23867     },
23868     {
23869         clsName: 'years',
23870         navFnc: 'FullYear',
23871         navStep: 10
23872     }]
23873 });
23874
23875 Roo.apply(Roo.bootstrap.form.DateField,  {
23876   
23877     template : {
23878         tag: 'div',
23879         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23880         cn: [
23881         {
23882             tag: 'div',
23883             cls: 'datepicker-days',
23884             cn: [
23885             {
23886                 tag: 'table',
23887                 cls: 'table-condensed',
23888                 cn:[
23889                 Roo.bootstrap.form.DateField.head,
23890                 {
23891                     tag: 'tbody'
23892                 },
23893                 Roo.bootstrap.form.DateField.footer
23894                 ]
23895             }
23896             ]
23897         },
23898         {
23899             tag: 'div',
23900             cls: 'datepicker-months',
23901             cn: [
23902             {
23903                 tag: 'table',
23904                 cls: 'table-condensed',
23905                 cn:[
23906                 Roo.bootstrap.form.DateField.head,
23907                 Roo.bootstrap.form.DateField.content,
23908                 Roo.bootstrap.form.DateField.footer
23909                 ]
23910             }
23911             ]
23912         },
23913         {
23914             tag: 'div',
23915             cls: 'datepicker-years',
23916             cn: [
23917             {
23918                 tag: 'table',
23919                 cls: 'table-condensed',
23920                 cn:[
23921                 Roo.bootstrap.form.DateField.head,
23922                 Roo.bootstrap.form.DateField.content,
23923                 Roo.bootstrap.form.DateField.footer
23924                 ]
23925             }
23926             ]
23927         }
23928         ]
23929     }
23930 });
23931
23932  
23933
23934  /*
23935  * - LGPL
23936  *
23937  * TimeField
23938  * 
23939  */
23940
23941 /**
23942  * @class Roo.bootstrap.form.TimeField
23943  * @extends Roo.bootstrap.form.Input
23944  * Bootstrap DateField class
23945  * 
23946  * 
23947  * @constructor
23948  * Create a new TimeField
23949  * @param {Object} config The config object
23950  */
23951
23952 Roo.bootstrap.form.TimeField = function(config){
23953     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23954     this.addEvents({
23955             /**
23956              * @event show
23957              * Fires when this field show.
23958              * @param {Roo.bootstrap.form.DateField} thisthis
23959              * @param {Mixed} date The date value
23960              */
23961             show : true,
23962             /**
23963              * @event show
23964              * Fires when this field hide.
23965              * @param {Roo.bootstrap.form.DateField} this
23966              * @param {Mixed} date The date value
23967              */
23968             hide : true,
23969             /**
23970              * @event select
23971              * Fires when select a date.
23972              * @param {Roo.bootstrap.form.DateField} this
23973              * @param {Mixed} date The date value
23974              */
23975             select : true
23976         });
23977 };
23978
23979 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23980     
23981     /**
23982      * @cfg {String} format
23983      * The default time format string which can be overriden for localization support.  The format must be
23984      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23985      */
23986     format : "H:i",
23987
23988     getAutoCreate : function()
23989     {
23990         this.after = '<i class="fa far fa-clock"></i>';
23991         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23992         
23993          
23994     },
23995     onRender: function(ct, position)
23996     {
23997         
23998         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23999                 
24000         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24001         
24002         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24003         
24004         this.pop = this.picker().select('>.datepicker-time',true).first();
24005         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.picker().on('mousedown', this.onMousedown, this);
24008         this.picker().on('click', this.onClick, this);
24009         
24010         this.picker().addClass('datepicker-dropdown');
24011     
24012         this.fillTime();
24013         this.update();
24014             
24015         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24016         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24017         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24018         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24019         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24020         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24021
24022     },
24023     
24024     fireKey: function(e){
24025         if (!this.picker().isVisible()){
24026             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027                 this.show();
24028             }
24029             return;
24030         }
24031
24032         e.preventDefault();
24033         
24034         switch(e.keyCode){
24035             case 27: // escape
24036                 this.hide();
24037                 break;
24038             case 37: // left
24039             case 39: // right
24040                 this.onTogglePeriod();
24041                 break;
24042             case 38: // up
24043                 this.onIncrementMinutes();
24044                 break;
24045             case 40: // down
24046                 this.onDecrementMinutes();
24047                 break;
24048             case 13: // enter
24049             case 9: // tab
24050                 this.setTime();
24051                 break;
24052         }
24053     },
24054     
24055     onClick: function(e) {
24056         e.stopPropagation();
24057         e.preventDefault();
24058     },
24059     
24060     picker : function()
24061     {
24062         return this.pickerEl;
24063     },
24064     
24065     fillTime: function()
24066     {    
24067         var time = this.pop.select('tbody', true).first();
24068         
24069         time.dom.innerHTML = '';
24070         
24071         time.createChild({
24072             tag: 'tr',
24073             cn: [
24074                 {
24075                     tag: 'td',
24076                     cn: [
24077                         {
24078                             tag: 'a',
24079                             href: '#',
24080                             cls: 'btn',
24081                             cn: [
24082                                 {
24083                                     tag: 'i',
24084                                     cls: 'hours-up fa fas fa-chevron-up'
24085                                 }
24086                             ]
24087                         } 
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator'
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cn: [
24097                         {
24098                             tag: 'a',
24099                             href: '#',
24100                             cls: 'btn',
24101                             cn: [
24102                                 {
24103                                     tag: 'i',
24104                                     cls: 'minutes-up fa fas fa-chevron-up'
24105                                 }
24106                             ]
24107                         }
24108                     ]
24109                 },
24110                 {
24111                     tag: 'td',
24112                     cls: 'separator'
24113                 }
24114             ]
24115         });
24116         
24117         time.createChild({
24118             tag: 'tr',
24119             cn: [
24120                 {
24121                     tag: 'td',
24122                     cn: [
24123                         {
24124                             tag: 'span',
24125                             cls: 'timepicker-hour',
24126                             html: '00'
24127                         }  
24128                     ]
24129                 },
24130                 {
24131                     tag: 'td',
24132                     cls: 'separator',
24133                     html: ':'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'span',
24140                             cls: 'timepicker-minute',
24141                             html: '00'
24142                         }  
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator'
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cn: [
24152                         {
24153                             tag: 'button',
24154                             type: 'button',
24155                             cls: 'btn btn-primary period',
24156                             html: 'AM'
24157                             
24158                         }
24159                     ]
24160                 }
24161             ]
24162         });
24163         
24164         time.createChild({
24165             tag: 'tr',
24166             cn: [
24167                 {
24168                     tag: 'td',
24169                     cn: [
24170                         {
24171                             tag: 'a',
24172                             href: '#',
24173                             cls: 'btn',
24174                             cn: [
24175                                 {
24176                                     tag: 'span',
24177                                     cls: 'hours-down fa fas fa-chevron-down'
24178                                 }
24179                             ]
24180                         }
24181                     ]
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cls: 'separator'
24186                 },
24187                 {
24188                     tag: 'td',
24189                     cn: [
24190                         {
24191                             tag: 'a',
24192                             href: '#',
24193                             cls: 'btn',
24194                             cn: [
24195                                 {
24196                                     tag: 'span',
24197                                     cls: 'minutes-down fa fas fa-chevron-down'
24198                                 }
24199                             ]
24200                         }
24201                     ]
24202                 },
24203                 {
24204                     tag: 'td',
24205                     cls: 'separator'
24206                 }
24207             ]
24208         });
24209         
24210     },
24211     
24212     update: function()
24213     {
24214         
24215         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24216         
24217         this.fill();
24218     },
24219     
24220     fill: function() 
24221     {
24222         var hours = this.time.getHours();
24223         var minutes = this.time.getMinutes();
24224         var period = 'AM';
24225         
24226         if(hours > 11){
24227             period = 'PM';
24228         }
24229         
24230         if(hours == 0){
24231             hours = 12;
24232         }
24233         
24234         
24235         if(hours > 12){
24236             hours = hours - 12;
24237         }
24238         
24239         if(hours < 10){
24240             hours = '0' + hours;
24241         }
24242         
24243         if(minutes < 10){
24244             minutes = '0' + minutes;
24245         }
24246         
24247         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24248         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24249         this.pop.select('button', true).first().dom.innerHTML = period;
24250         
24251     },
24252     
24253     place: function()
24254     {   
24255         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24256         
24257         var cls = ['bottom'];
24258         
24259         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24260             cls.pop();
24261             cls.push('top');
24262         }
24263         
24264         cls.push('right');
24265         
24266         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24267             cls.pop();
24268             cls.push('left');
24269         }
24270         //this.picker().setXY(20000,20000);
24271         this.picker().addClass(cls.join('-'));
24272         
24273         var _this = this;
24274         
24275         Roo.each(cls, function(c){
24276             if(c == 'bottom'){
24277                 (function() {
24278                  //  
24279                 }).defer(200);
24280                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24281                 //_this.picker().setTop(_this.inputEl().getHeight());
24282                 return;
24283             }
24284             if(c == 'top'){
24285                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24286                 
24287                 //_this.picker().setTop(0 - _this.picker().getHeight());
24288                 return;
24289             }
24290             /*
24291             if(c == 'left'){
24292                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24293                 return;
24294             }
24295             if(c == 'right'){
24296                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24297                 return;
24298             }
24299             */
24300         });
24301         
24302     },
24303   
24304     onFocus : function()
24305     {
24306         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24307         this.show();
24308     },
24309     
24310     onBlur : function()
24311     {
24312         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24313         this.hide();
24314     },
24315     
24316     show : function()
24317     {
24318         this.picker().show();
24319         this.pop.show();
24320         this.update();
24321         this.place();
24322         
24323         this.fireEvent('show', this, this.date);
24324     },
24325     
24326     hide : function()
24327     {
24328         this.picker().hide();
24329         this.pop.hide();
24330         
24331         this.fireEvent('hide', this, this.date);
24332     },
24333     
24334     setTime : function()
24335     {
24336         this.hide();
24337         this.setValue(this.time.format(this.format));
24338         
24339         this.fireEvent('select', this, this.date);
24340         
24341         
24342     },
24343     
24344     onMousedown: function(e){
24345         e.stopPropagation();
24346         e.preventDefault();
24347     },
24348     
24349     onIncrementHours: function()
24350     {
24351         Roo.log('onIncrementHours');
24352         this.time = this.time.add(Date.HOUR, 1);
24353         this.update();
24354         
24355     },
24356     
24357     onDecrementHours: function()
24358     {
24359         Roo.log('onDecrementHours');
24360         this.time = this.time.add(Date.HOUR, -1);
24361         this.update();
24362     },
24363     
24364     onIncrementMinutes: function()
24365     {
24366         Roo.log('onIncrementMinutes');
24367         this.time = this.time.add(Date.MINUTE, 1);
24368         this.update();
24369     },
24370     
24371     onDecrementMinutes: function()
24372     {
24373         Roo.log('onDecrementMinutes');
24374         this.time = this.time.add(Date.MINUTE, -1);
24375         this.update();
24376     },
24377     
24378     onTogglePeriod: function()
24379     {
24380         Roo.log('onTogglePeriod');
24381         this.time = this.time.add(Date.HOUR, 12);
24382         this.update();
24383     }
24384     
24385    
24386 });
24387  
24388
24389 Roo.apply(Roo.bootstrap.form.TimeField,  {
24390   
24391     template : {
24392         tag: 'div',
24393         cls: 'datepicker dropdown-menu',
24394         cn: [
24395             {
24396                 tag: 'div',
24397                 cls: 'datepicker-time',
24398                 cn: [
24399                 {
24400                     tag: 'table',
24401                     cls: 'table-condensed',
24402                     cn:[
24403                         {
24404                             tag: 'tbody',
24405                             cn: [
24406                                 {
24407                                     tag: 'tr',
24408                                     cn: [
24409                                     {
24410                                         tag: 'td',
24411                                         colspan: '7'
24412                                     }
24413                                     ]
24414                                 }
24415                             ]
24416                         },
24417                         {
24418                             tag: 'tfoot',
24419                             cn: [
24420                                 {
24421                                     tag: 'tr',
24422                                     cn: [
24423                                     {
24424                                         tag: 'th',
24425                                         colspan: '7',
24426                                         cls: '',
24427                                         cn: [
24428                                             {
24429                                                 tag: 'button',
24430                                                 cls: 'btn btn-info ok',
24431                                                 html: 'OK'
24432                                             }
24433                                         ]
24434                                     }
24435                     
24436                                     ]
24437                                 }
24438                             ]
24439                         }
24440                     ]
24441                 }
24442                 ]
24443             }
24444         ]
24445     }
24446 });
24447
24448  
24449
24450  /*
24451  * - LGPL
24452  *
24453  * MonthField
24454  * 
24455  */
24456
24457 /**
24458  * @class Roo.bootstrap.form.MonthField
24459  * @extends Roo.bootstrap.form.Input
24460  * Bootstrap MonthField class
24461  * 
24462  * @cfg {String} language default en
24463  * 
24464  * @constructor
24465  * Create a new MonthField
24466  * @param {Object} config The config object
24467  */
24468
24469 Roo.bootstrap.form.MonthField = function(config){
24470     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24471     
24472     this.addEvents({
24473         /**
24474          * @event show
24475          * Fires when this field show.
24476          * @param {Roo.bootstrap.form.MonthField} this
24477          * @param {Mixed} date The date value
24478          */
24479         show : true,
24480         /**
24481          * @event show
24482          * Fires when this field hide.
24483          * @param {Roo.bootstrap.form.MonthField} this
24484          * @param {Mixed} date The date value
24485          */
24486         hide : true,
24487         /**
24488          * @event select
24489          * Fires when select a date.
24490          * @param {Roo.bootstrap.form.MonthField} this
24491          * @param {String} oldvalue The old value
24492          * @param {String} newvalue The new value
24493          */
24494         select : true
24495     });
24496 };
24497
24498 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24499     
24500     onRender: function(ct, position)
24501     {
24502         
24503         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24504         
24505         this.language = this.language || 'en';
24506         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24507         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24508         
24509         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24510         this.isInline = false;
24511         this.isInput = true;
24512         this.component = this.el.select('.add-on', true).first() || false;
24513         this.component = (this.component && this.component.length === 0) ? false : this.component;
24514         this.hasInput = this.component && this.inputEL().length;
24515         
24516         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24517         
24518         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24519         
24520         this.picker().on('mousedown', this.onMousedown, this);
24521         this.picker().on('click', this.onClick, this);
24522         
24523         this.picker().addClass('datepicker-dropdown');
24524         
24525         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24526             v.setStyle('width', '189px');
24527         });
24528         
24529         this.fillMonths();
24530         
24531         this.update();
24532         
24533         if(this.isInline) {
24534             this.show();
24535         }
24536         
24537     },
24538     
24539     setValue: function(v, suppressEvent)
24540     {   
24541         var o = this.getValue();
24542         
24543         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24544         
24545         this.update();
24546
24547         if(suppressEvent !== true){
24548             this.fireEvent('select', this, o, v);
24549         }
24550         
24551     },
24552     
24553     getValue: function()
24554     {
24555         return this.value;
24556     },
24557     
24558     onClick: function(e) 
24559     {
24560         e.stopPropagation();
24561         e.preventDefault();
24562         
24563         var target = e.getTarget();
24564         
24565         if(target.nodeName.toLowerCase() === 'i'){
24566             target = Roo.get(target).dom.parentNode;
24567         }
24568         
24569         var nodeName = target.nodeName;
24570         var className = target.className;
24571         var html = target.innerHTML;
24572         
24573         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24574             return;
24575         }
24576         
24577         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24578         
24579         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24580         
24581         this.hide();
24582                         
24583     },
24584     
24585     picker : function()
24586     {
24587         return this.pickerEl;
24588     },
24589     
24590     fillMonths: function()
24591     {    
24592         var i = 0;
24593         var months = this.picker().select('>.datepicker-months td', true).first();
24594         
24595         months.dom.innerHTML = '';
24596         
24597         while (i < 12) {
24598             var month = {
24599                 tag: 'span',
24600                 cls: 'month',
24601                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24602             };
24603             
24604             months.createChild(month);
24605         }
24606         
24607     },
24608     
24609     update: function()
24610     {
24611         var _this = this;
24612         
24613         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24614             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24615         }
24616         
24617         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24618             e.removeClass('active');
24619             
24620             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24621                 e.addClass('active');
24622             }
24623         })
24624     },
24625     
24626     place: function()
24627     {
24628         if(this.isInline) {
24629             return;
24630         }
24631         
24632         this.picker().removeClass(['bottom', 'top']);
24633         
24634         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24635             /*
24636              * place to the top of element!
24637              *
24638              */
24639             
24640             this.picker().addClass('top');
24641             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24642             
24643             return;
24644         }
24645         
24646         this.picker().addClass('bottom');
24647         
24648         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24649     },
24650     
24651     onFocus : function()
24652     {
24653         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24654         this.show();
24655     },
24656     
24657     onBlur : function()
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24660         
24661         var d = this.inputEl().getValue();
24662         
24663         this.setValue(d);
24664                 
24665         this.hide();
24666     },
24667     
24668     show : function()
24669     {
24670         this.picker().show();
24671         this.picker().select('>.datepicker-months', true).first().show();
24672         this.update();
24673         this.place();
24674         
24675         this.fireEvent('show', this, this.date);
24676     },
24677     
24678     hide : function()
24679     {
24680         if(this.isInline) {
24681             return;
24682         }
24683         this.picker().hide();
24684         this.fireEvent('hide', this, this.date);
24685         
24686     },
24687     
24688     onMousedown: function(e)
24689     {
24690         e.stopPropagation();
24691         e.preventDefault();
24692     },
24693     
24694     keyup: function(e)
24695     {
24696         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24697         this.update();
24698     },
24699
24700     fireKey: function(e)
24701     {
24702         if (!this.picker().isVisible()){
24703             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24704                 this.show();
24705             }
24706             return;
24707         }
24708         
24709         var dir;
24710         
24711         switch(e.keyCode){
24712             case 27: // escape
24713                 this.hide();
24714                 e.preventDefault();
24715                 break;
24716             case 37: // left
24717             case 39: // right
24718                 dir = e.keyCode == 37 ? -1 : 1;
24719                 
24720                 this.vIndex = this.vIndex + dir;
24721                 
24722                 if(this.vIndex < 0){
24723                     this.vIndex = 0;
24724                 }
24725                 
24726                 if(this.vIndex > 11){
24727                     this.vIndex = 11;
24728                 }
24729                 
24730                 if(isNaN(this.vIndex)){
24731                     this.vIndex = 0;
24732                 }
24733                 
24734                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24735                 
24736                 break;
24737             case 38: // up
24738             case 40: // down
24739                 
24740                 dir = e.keyCode == 38 ? -1 : 1;
24741                 
24742                 this.vIndex = this.vIndex + dir * 4;
24743                 
24744                 if(this.vIndex < 0){
24745                     this.vIndex = 0;
24746                 }
24747                 
24748                 if(this.vIndex > 11){
24749                     this.vIndex = 11;
24750                 }
24751                 
24752                 if(isNaN(this.vIndex)){
24753                     this.vIndex = 0;
24754                 }
24755                 
24756                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24757                 break;
24758                 
24759             case 13: // enter
24760                 
24761                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24762                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 }
24764                 
24765                 this.hide();
24766                 e.preventDefault();
24767                 break;
24768             case 9: // tab
24769                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24770                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24771                 }
24772                 this.hide();
24773                 break;
24774             case 16: // shift
24775             case 17: // ctrl
24776             case 18: // alt
24777                 break;
24778             default :
24779                 this.hide();
24780                 
24781         }
24782     },
24783     
24784     remove: function() 
24785     {
24786         this.picker().remove();
24787     }
24788    
24789 });
24790
24791 Roo.apply(Roo.bootstrap.form.MonthField,  {
24792     
24793     content : {
24794         tag: 'tbody',
24795         cn: [
24796         {
24797             tag: 'tr',
24798             cn: [
24799             {
24800                 tag: 'td',
24801                 colspan: '7'
24802             }
24803             ]
24804         }
24805         ]
24806     },
24807     
24808     dates:{
24809         en: {
24810             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24811             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24812         }
24813     }
24814 });
24815
24816 Roo.apply(Roo.bootstrap.form.MonthField,  {
24817   
24818     template : {
24819         tag: 'div',
24820         cls: 'datepicker dropdown-menu roo-dynamic',
24821         cn: [
24822             {
24823                 tag: 'div',
24824                 cls: 'datepicker-months',
24825                 cn: [
24826                 {
24827                     tag: 'table',
24828                     cls: 'table-condensed',
24829                     cn:[
24830                         Roo.bootstrap.form.DateField.content
24831                     ]
24832                 }
24833                 ]
24834             }
24835         ]
24836     }
24837 });
24838
24839  
24840
24841  
24842  /*
24843  * - LGPL
24844  *
24845  * CheckBox
24846  * 
24847  */
24848
24849 /**
24850  * @class Roo.bootstrap.form.CheckBox
24851  * @extends Roo.bootstrap.form.Input
24852  * Bootstrap CheckBox class
24853  * 
24854  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24855  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24856  * @cfg {String} boxLabel The text that appears beside the checkbox
24857  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24858  * @cfg {Boolean} checked initnal the element
24859  * @cfg {Boolean} inline inline the element (default false)
24860  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24861  * @cfg {String} tooltip label tooltip
24862  * 
24863  * @constructor
24864  * Create a new CheckBox
24865  * @param {Object} config The config object
24866  */
24867
24868 Roo.bootstrap.form.CheckBox = function(config){
24869     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24870    
24871     this.addEvents({
24872         /**
24873         * @event check
24874         * Fires when the element is checked or unchecked.
24875         * @param {Roo.bootstrap.form.CheckBox} this This input
24876         * @param {Boolean} checked The new checked value
24877         */
24878        check : true,
24879        /**
24880         * @event click
24881         * Fires when the element is click.
24882         * @param {Roo.bootstrap.form.CheckBox} this This input
24883         */
24884        click : true
24885     });
24886     
24887 };
24888
24889 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24890   
24891     inputType: 'checkbox',
24892     inputValue: 1,
24893     valueOff: 0,
24894     boxLabel: false,
24895     checked: false,
24896     weight : false,
24897     inline: false,
24898     tooltip : '',
24899     
24900     // checkbox success does not make any sense really.. 
24901     invalidClass : "",
24902     validClass : "",
24903     
24904     
24905     getAutoCreate : function()
24906     {
24907         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24908         
24909         var id = Roo.id();
24910         
24911         var cfg = {};
24912         
24913         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24914         
24915         if(this.inline){
24916             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24917         }
24918         
24919         var input =  {
24920             tag: 'input',
24921             id : id,
24922             type : this.inputType,
24923             value : this.inputValue,
24924             cls : 'roo-' + this.inputType, //'form-box',
24925             placeholder : this.placeholder || ''
24926             
24927         };
24928         
24929         if(this.inputType != 'radio'){
24930             var hidden =  {
24931                 tag: 'input',
24932                 type : 'hidden',
24933                 cls : 'roo-hidden-value',
24934                 value : this.checked ? this.inputValue : this.valueOff
24935             };
24936         }
24937         
24938             
24939         if (this.weight) { // Validity check?
24940             cfg.cls += " " + this.inputType + "-" + this.weight;
24941         }
24942         
24943         if (this.disabled) {
24944             input.disabled=true;
24945         }
24946         
24947         if(this.checked){
24948             input.checked = this.checked;
24949         }
24950         
24951         if (this.name) {
24952             
24953             input.name = this.name;
24954             
24955             if(this.inputType != 'radio'){
24956                 hidden.name = this.name;
24957                 input.name = '_hidden_' + this.name;
24958             }
24959         }
24960         
24961         if (this.size) {
24962             input.cls += ' input-' + this.size;
24963         }
24964         
24965         var settings=this;
24966         
24967         ['xs','sm','md','lg'].map(function(size){
24968             if (settings[size]) {
24969                 cfg.cls += ' col-' + size + '-' + settings[size];
24970             }
24971         });
24972         
24973         var inputblock = input;
24974          
24975         if (this.before || this.after) {
24976             
24977             inputblock = {
24978                 cls : 'input-group',
24979                 cn :  [] 
24980             };
24981             
24982             if (this.before) {
24983                 inputblock.cn.push({
24984                     tag :'span',
24985                     cls : 'input-group-addon',
24986                     html : this.before
24987                 });
24988             }
24989             
24990             inputblock.cn.push(input);
24991             
24992             if(this.inputType != 'radio'){
24993                 inputblock.cn.push(hidden);
24994             }
24995             
24996             if (this.after) {
24997                 inputblock.cn.push({
24998                     tag :'span',
24999                     cls : 'input-group-addon',
25000                     html : this.after
25001                 });
25002             }
25003             
25004         }
25005         var boxLabelCfg = false;
25006         
25007         if(this.boxLabel){
25008            
25009             boxLabelCfg = {
25010                 tag: 'label',
25011                 //'for': id, // box label is handled by onclick - so no for...
25012                 cls: 'box-label',
25013                 html: this.boxLabel
25014             };
25015             if(this.tooltip){
25016                 boxLabelCfg.tooltip = this.tooltip;
25017             }
25018              
25019         }
25020         
25021         
25022         if (align ==='left' && this.fieldLabel.length) {
25023 //                Roo.log("left and has label");
25024             cfg.cn = [
25025                 {
25026                     tag: 'label',
25027                     'for' :  id,
25028                     cls : 'control-label',
25029                     html : this.fieldLabel
25030                 },
25031                 {
25032                     cls : "", 
25033                     cn: [
25034                         inputblock
25035                     ]
25036                 }
25037             ];
25038             
25039             if (boxLabelCfg) {
25040                 cfg.cn[1].cn.push(boxLabelCfg);
25041             }
25042             
25043             if(this.labelWidth > 12){
25044                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25045             }
25046             
25047             if(this.labelWidth < 13 && this.labelmd == 0){
25048                 this.labelmd = this.labelWidth;
25049             }
25050             
25051             if(this.labellg > 0){
25052                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25053                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25054             }
25055             
25056             if(this.labelmd > 0){
25057                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25058                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25059             }
25060             
25061             if(this.labelsm > 0){
25062                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25063                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25064             }
25065             
25066             if(this.labelxs > 0){
25067                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25068                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25069             }
25070             
25071         } else if ( this.fieldLabel.length) {
25072 //                Roo.log(" label");
25073                 cfg.cn = [
25074                    
25075                     {
25076                         tag: this.boxLabel ? 'span' : 'label',
25077                         'for': id,
25078                         cls: 'control-label box-input-label',
25079                         //cls : 'input-group-addon',
25080                         html : this.fieldLabel
25081                     },
25082                     
25083                     inputblock
25084                     
25085                 ];
25086                 if (boxLabelCfg) {
25087                     cfg.cn.push(boxLabelCfg);
25088                 }
25089
25090         } else {
25091             
25092 //                Roo.log(" no label && no align");
25093                 cfg.cn = [  inputblock ] ;
25094                 if (boxLabelCfg) {
25095                     cfg.cn.push(boxLabelCfg);
25096                 }
25097
25098                 
25099         }
25100         
25101        
25102         
25103         if(this.inputType != 'radio'){
25104             cfg.cn.push(hidden);
25105         }
25106         
25107         return cfg;
25108         
25109     },
25110     
25111     /**
25112      * return the real input element.
25113      */
25114     inputEl: function ()
25115     {
25116         return this.el.select('input.roo-' + this.inputType,true).first();
25117     },
25118     hiddenEl: function ()
25119     {
25120         return this.el.select('input.roo-hidden-value',true).first();
25121     },
25122     
25123     labelEl: function()
25124     {
25125         return this.el.select('label.control-label',true).first();
25126     },
25127     /* depricated... */
25128     
25129     label: function()
25130     {
25131         return this.labelEl();
25132     },
25133     
25134     boxLabelEl: function()
25135     {
25136         return this.el.select('label.box-label',true).first();
25137     },
25138     
25139     initEvents : function()
25140     {
25141 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25142         
25143         this.inputEl().on('click', this.onClick,  this);
25144         
25145         if (this.boxLabel) { 
25146             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25147         }
25148         
25149         this.startValue = this.getValue();
25150         
25151         if(this.groupId){
25152             Roo.bootstrap.form.CheckBox.register(this);
25153         }
25154     },
25155     
25156     onClick : function(e)
25157     {   
25158         if(this.fireEvent('click', this, e) !== false){
25159             this.setChecked(!this.checked);
25160         }
25161         
25162     },
25163     
25164     setChecked : function(state,suppressEvent)
25165     {
25166         this.startValue = this.getValue();
25167
25168         if(this.inputType == 'radio'){
25169             
25170             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25171                 e.dom.checked = false;
25172             });
25173             
25174             this.inputEl().dom.checked = true;
25175             
25176             this.inputEl().dom.value = this.inputValue;
25177             
25178             if(suppressEvent !== true){
25179                 this.fireEvent('check', this, true);
25180             }
25181             
25182             this.validate();
25183             
25184             return;
25185         }
25186         
25187         this.checked = state;
25188         
25189         this.inputEl().dom.checked = state;
25190         
25191         
25192         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25193         
25194         if(suppressEvent !== true){
25195             this.fireEvent('check', this, state);
25196         }
25197         
25198         this.validate();
25199     },
25200     
25201     getValue : function()
25202     {
25203         if(this.inputType == 'radio'){
25204             return this.getGroupValue();
25205         }
25206         
25207         return this.hiddenEl().dom.value;
25208         
25209     },
25210     
25211     getGroupValue : function()
25212     {
25213         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25214             return '';
25215         }
25216         
25217         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25218     },
25219     
25220     setValue : function(v,suppressEvent)
25221     {
25222         if(this.inputType == 'radio'){
25223             this.setGroupValue(v, suppressEvent);
25224             return;
25225         }
25226         
25227         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25228         
25229         this.validate();
25230     },
25231     
25232     setGroupValue : function(v, suppressEvent)
25233     {
25234         this.startValue = this.getValue();
25235         
25236         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25237             e.dom.checked = false;
25238             
25239             if(e.dom.value == v){
25240                 e.dom.checked = true;
25241             }
25242         });
25243         
25244         if(suppressEvent !== true){
25245             this.fireEvent('check', this, true);
25246         }
25247
25248         this.validate();
25249         
25250         return;
25251     },
25252     
25253     validate : function()
25254     {
25255         if(this.getVisibilityEl().hasClass('hidden')){
25256             return true;
25257         }
25258         
25259         if(
25260                 this.disabled || 
25261                 (this.inputType == 'radio' && this.validateRadio()) ||
25262                 (this.inputType == 'checkbox' && this.validateCheckbox())
25263         ){
25264             this.markValid();
25265             return true;
25266         }
25267         
25268         this.markInvalid();
25269         return false;
25270     },
25271     
25272     validateRadio : function()
25273     {
25274         if(this.getVisibilityEl().hasClass('hidden')){
25275             return true;
25276         }
25277         
25278         if(this.allowBlank){
25279             return true;
25280         }
25281         
25282         var valid = false;
25283         
25284         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25285             if(!e.dom.checked){
25286                 return;
25287             }
25288             
25289             valid = true;
25290             
25291             return false;
25292         });
25293         
25294         return valid;
25295     },
25296     
25297     validateCheckbox : function()
25298     {
25299         if(!this.groupId){
25300             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25301             //return (this.getValue() == this.inputValue) ? true : false;
25302         }
25303         
25304         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25305         
25306         if(!group){
25307             return false;
25308         }
25309         
25310         var r = false;
25311         
25312         for(var i in group){
25313             if(group[i].el.isVisible(true)){
25314                 r = false;
25315                 break;
25316             }
25317             
25318             r = true;
25319         }
25320         
25321         for(var i in group){
25322             if(r){
25323                 break;
25324             }
25325             
25326             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25327         }
25328         
25329         return r;
25330     },
25331     
25332     /**
25333      * Mark this field as valid
25334      */
25335     markValid : function()
25336     {
25337         var _this = this;
25338         
25339         this.fireEvent('valid', this);
25340         
25341         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25342         
25343         if(this.groupId){
25344             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25345         }
25346         
25347         if(label){
25348             label.markValid();
25349         }
25350
25351         if(this.inputType == 'radio'){
25352             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25353                 var fg = e.findParent('.form-group', false, true);
25354                 if (Roo.bootstrap.version == 3) {
25355                     fg.removeClass([_this.invalidClass, _this.validClass]);
25356                     fg.addClass(_this.validClass);
25357                 } else {
25358                     fg.removeClass(['is-valid', 'is-invalid']);
25359                     fg.addClass('is-valid');
25360                 }
25361             });
25362             
25363             return;
25364         }
25365
25366         if(!this.groupId){
25367             var fg = this.el.findParent('.form-group', false, true);
25368             if (Roo.bootstrap.version == 3) {
25369                 fg.removeClass([this.invalidClass, this.validClass]);
25370                 fg.addClass(this.validClass);
25371             } else {
25372                 fg.removeClass(['is-valid', 'is-invalid']);
25373                 fg.addClass('is-valid');
25374             }
25375             return;
25376         }
25377         
25378         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25379         
25380         if(!group){
25381             return;
25382         }
25383         
25384         for(var i in group){
25385             var fg = group[i].el.findParent('.form-group', false, true);
25386             if (Roo.bootstrap.version == 3) {
25387                 fg.removeClass([this.invalidClass, this.validClass]);
25388                 fg.addClass(this.validClass);
25389             } else {
25390                 fg.removeClass(['is-valid', 'is-invalid']);
25391                 fg.addClass('is-valid');
25392             }
25393         }
25394     },
25395     
25396      /**
25397      * Mark this field as invalid
25398      * @param {String} msg The validation message
25399      */
25400     markInvalid : function(msg)
25401     {
25402         if(this.allowBlank){
25403             return;
25404         }
25405         
25406         var _this = this;
25407         
25408         this.fireEvent('invalid', this, msg);
25409         
25410         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25411         
25412         if(this.groupId){
25413             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25414         }
25415         
25416         if(label){
25417             label.markInvalid();
25418         }
25419             
25420         if(this.inputType == 'radio'){
25421             
25422             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25423                 var fg = e.findParent('.form-group', false, true);
25424                 if (Roo.bootstrap.version == 3) {
25425                     fg.removeClass([_this.invalidClass, _this.validClass]);
25426                     fg.addClass(_this.invalidClass);
25427                 } else {
25428                     fg.removeClass(['is-invalid', 'is-valid']);
25429                     fg.addClass('is-invalid');
25430                 }
25431             });
25432             
25433             return;
25434         }
25435         
25436         if(!this.groupId){
25437             var fg = this.el.findParent('.form-group', false, true);
25438             if (Roo.bootstrap.version == 3) {
25439                 fg.removeClass([_this.invalidClass, _this.validClass]);
25440                 fg.addClass(_this.invalidClass);
25441             } else {
25442                 fg.removeClass(['is-invalid', 'is-valid']);
25443                 fg.addClass('is-invalid');
25444             }
25445             return;
25446         }
25447         
25448         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25449         
25450         if(!group){
25451             return;
25452         }
25453         
25454         for(var i in group){
25455             var fg = group[i].el.findParent('.form-group', false, true);
25456             if (Roo.bootstrap.version == 3) {
25457                 fg.removeClass([_this.invalidClass, _this.validClass]);
25458                 fg.addClass(_this.invalidClass);
25459             } else {
25460                 fg.removeClass(['is-invalid', 'is-valid']);
25461                 fg.addClass('is-invalid');
25462             }
25463         }
25464         
25465     },
25466     
25467     clearInvalid : function()
25468     {
25469         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25470         
25471         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25472         
25473         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25474         
25475         if (label && label.iconEl) {
25476             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25477             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25478         }
25479     },
25480     
25481     disable : function()
25482     {
25483         if(this.inputType != 'radio'){
25484             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25485             return;
25486         }
25487         
25488         var _this = this;
25489         
25490         if(this.rendered){
25491             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25492                 _this.getActionEl().addClass(this.disabledClass);
25493                 e.dom.disabled = true;
25494             });
25495         }
25496         
25497         this.disabled = true;
25498         this.fireEvent("disable", this);
25499         return this;
25500     },
25501
25502     enable : function()
25503     {
25504         if(this.inputType != 'radio'){
25505             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25506             return;
25507         }
25508         
25509         var _this = this;
25510         
25511         if(this.rendered){
25512             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25513                 _this.getActionEl().removeClass(this.disabledClass);
25514                 e.dom.disabled = false;
25515             });
25516         }
25517         
25518         this.disabled = false;
25519         this.fireEvent("enable", this);
25520         return this;
25521     },
25522     
25523     setBoxLabel : function(v)
25524     {
25525         this.boxLabel = v;
25526         
25527         if(this.rendered){
25528             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25529         }
25530     }
25531
25532 });
25533
25534 Roo.apply(Roo.bootstrap.form.CheckBox, {
25535     
25536     groups: {},
25537     
25538      /**
25539     * register a CheckBox Group
25540     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25541     */
25542     register : function(checkbox)
25543     {
25544         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25545             this.groups[checkbox.groupId] = {};
25546         }
25547         
25548         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25549             return;
25550         }
25551         
25552         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25553         
25554     },
25555     /**
25556     * fetch a CheckBox Group based on the group ID
25557     * @param {string} the group ID
25558     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25559     */
25560     get: function(groupId) {
25561         if (typeof(this.groups[groupId]) == 'undefined') {
25562             return false;
25563         }
25564         
25565         return this.groups[groupId] ;
25566     }
25567     
25568     
25569 });
25570 /*
25571  * - LGPL
25572  *
25573  * RadioItem
25574  * 
25575  */
25576
25577 /**
25578  * @class Roo.bootstrap.form.Radio
25579  * @extends Roo.bootstrap.Component
25580  * Bootstrap Radio class
25581  * @cfg {String} boxLabel - the label associated
25582  * @cfg {String} value - the value of radio
25583  * 
25584  * @constructor
25585  * Create a new Radio
25586  * @param {Object} config The config object
25587  */
25588 Roo.bootstrap.form.Radio = function(config){
25589     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25590     
25591 };
25592
25593 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25594     
25595     boxLabel : '',
25596     
25597     value : '',
25598     
25599     getAutoCreate : function()
25600     {
25601         var cfg = {
25602             tag : 'div',
25603             cls : 'form-group radio',
25604             cn : [
25605                 {
25606                     tag : 'label',
25607                     cls : 'box-label',
25608                     html : this.boxLabel
25609                 }
25610             ]
25611         };
25612         
25613         return cfg;
25614     },
25615     
25616     initEvents : function() 
25617     {
25618         this.parent().register(this);
25619         
25620         this.el.on('click', this.onClick, this);
25621         
25622     },
25623     
25624     onClick : function(e)
25625     {
25626         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25627             this.setChecked(true);
25628         }
25629     },
25630     
25631     setChecked : function(state, suppressEvent)
25632     {
25633         this.parent().setValue(this.value, suppressEvent);
25634         
25635     },
25636     
25637     setBoxLabel : function(v)
25638     {
25639         this.boxLabel = v;
25640         
25641         if(this.rendered){
25642             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25643         }
25644     }
25645     
25646 });
25647  
25648
25649  /*
25650  * - LGPL
25651  *
25652  * Input
25653  * 
25654  */
25655
25656 /**
25657  * @class Roo.bootstrap.form.SecurePass
25658  * @extends Roo.bootstrap.form.Input
25659  * Bootstrap SecurePass class
25660  *
25661  * 
25662  * @constructor
25663  * Create a new SecurePass
25664  * @param {Object} config The config object
25665  */
25666  
25667 Roo.bootstrap.form.SecurePass = function (config) {
25668     // these go here, so the translation tool can replace them..
25669     this.errors = {
25670         PwdEmpty: "Please type a password, and then retype it to confirm.",
25671         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25672         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25673         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25674         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25675         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25676         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25677         TooWeak: "Your password is Too Weak."
25678     },
25679     this.meterLabel = "Password strength:";
25680     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25681     this.meterClass = [
25682         "roo-password-meter-tooweak", 
25683         "roo-password-meter-weak", 
25684         "roo-password-meter-medium", 
25685         "roo-password-meter-strong", 
25686         "roo-password-meter-grey"
25687     ];
25688     
25689     this.errors = {};
25690     
25691     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25692 }
25693
25694 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25695     /**
25696      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25697      * {
25698      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25699      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25700      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25701      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25702      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25703      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25704      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25705      * })
25706      */
25707     // private
25708     
25709     meterWidth: 300,
25710     errorMsg :'',    
25711     errors: false,
25712     imageRoot: '/',
25713     /**
25714      * @cfg {String/Object} Label for the strength meter (defaults to
25715      * 'Password strength:')
25716      */
25717     // private
25718     meterLabel: '',
25719     /**
25720      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25721      * ['Weak', 'Medium', 'Strong'])
25722      */
25723     // private    
25724     pwdStrengths: false,    
25725     // private
25726     strength: 0,
25727     // private
25728     _lastPwd: null,
25729     // private
25730     kCapitalLetter: 0,
25731     kSmallLetter: 1,
25732     kDigit: 2,
25733     kPunctuation: 3,
25734     
25735     insecure: false,
25736     // private
25737     initEvents: function ()
25738     {
25739         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25740
25741         if (this.el.is('input[type=password]') && Roo.isSafari) {
25742             this.el.on('keydown', this.SafariOnKeyDown, this);
25743         }
25744
25745         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25746     },
25747     // private
25748     onRender: function (ct, position)
25749     {
25750         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25751         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25752         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25753
25754         this.trigger.createChild({
25755                    cn: [
25756                     {
25757                     //id: 'PwdMeter',
25758                     tag: 'div',
25759                     cls: 'roo-password-meter-grey col-xs-12',
25760                     style: {
25761                         //width: 0,
25762                         //width: this.meterWidth + 'px'                                                
25763                         }
25764                     },
25765                     {                            
25766                          cls: 'roo-password-meter-text'                          
25767                     }
25768                 ]            
25769         });
25770
25771          
25772         if (this.hideTrigger) {
25773             this.trigger.setDisplayed(false);
25774         }
25775         this.setSize(this.width || '', this.height || '');
25776     },
25777     // private
25778     onDestroy: function ()
25779     {
25780         if (this.trigger) {
25781             this.trigger.removeAllListeners();
25782             this.trigger.remove();
25783         }
25784         if (this.wrap) {
25785             this.wrap.remove();
25786         }
25787         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25788     },
25789     // private
25790     checkStrength: function ()
25791     {
25792         var pwd = this.inputEl().getValue();
25793         if (pwd == this._lastPwd) {
25794             return;
25795         }
25796
25797         var strength;
25798         if (this.ClientSideStrongPassword(pwd)) {
25799             strength = 3;
25800         } else if (this.ClientSideMediumPassword(pwd)) {
25801             strength = 2;
25802         } else if (this.ClientSideWeakPassword(pwd)) {
25803             strength = 1;
25804         } else {
25805             strength = 0;
25806         }
25807         
25808         Roo.log('strength1: ' + strength);
25809         
25810         //var pm = this.trigger.child('div/div/div').dom;
25811         var pm = this.trigger.child('div/div');
25812         pm.removeClass(this.meterClass);
25813         pm.addClass(this.meterClass[strength]);
25814                 
25815         
25816         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25817                 
25818         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25819         
25820         this._lastPwd = pwd;
25821     },
25822     reset: function ()
25823     {
25824         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25825         
25826         this._lastPwd = '';
25827         
25828         var pm = this.trigger.child('div/div');
25829         pm.removeClass(this.meterClass);
25830         pm.addClass('roo-password-meter-grey');        
25831         
25832         
25833         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25834         
25835         pt.innerHTML = '';
25836         this.inputEl().dom.type='password';
25837     },
25838     // private
25839     validateValue: function (value)
25840     {
25841         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25842             return false;
25843         }
25844         if (value.length == 0) {
25845             if (this.allowBlank) {
25846                 this.clearInvalid();
25847                 return true;
25848             }
25849
25850             this.markInvalid(this.errors.PwdEmpty);
25851             this.errorMsg = this.errors.PwdEmpty;
25852             return false;
25853         }
25854         
25855         if(this.insecure){
25856             return true;
25857         }
25858         
25859         if (!value.match(/[\x21-\x7e]+/)) {
25860             this.markInvalid(this.errors.PwdBadChar);
25861             this.errorMsg = this.errors.PwdBadChar;
25862             return false;
25863         }
25864         if (value.length < 6) {
25865             this.markInvalid(this.errors.PwdShort);
25866             this.errorMsg = this.errors.PwdShort;
25867             return false;
25868         }
25869         if (value.length > 16) {
25870             this.markInvalid(this.errors.PwdLong);
25871             this.errorMsg = this.errors.PwdLong;
25872             return false;
25873         }
25874         var strength;
25875         if (this.ClientSideStrongPassword(value)) {
25876             strength = 3;
25877         } else if (this.ClientSideMediumPassword(value)) {
25878             strength = 2;
25879         } else if (this.ClientSideWeakPassword(value)) {
25880             strength = 1;
25881         } else {
25882             strength = 0;
25883         }
25884
25885         
25886         if (strength < 2) {
25887             //this.markInvalid(this.errors.TooWeak);
25888             this.errorMsg = this.errors.TooWeak;
25889             //return false;
25890         }
25891         
25892         
25893         console.log('strength2: ' + strength);
25894         
25895         //var pm = this.trigger.child('div/div/div').dom;
25896         
25897         var pm = this.trigger.child('div/div');
25898         pm.removeClass(this.meterClass);
25899         pm.addClass(this.meterClass[strength]);
25900                 
25901         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25902                 
25903         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25904         
25905         this.errorMsg = ''; 
25906         return true;
25907     },
25908     // private
25909     CharacterSetChecks: function (type)
25910     {
25911         this.type = type;
25912         this.fResult = false;
25913     },
25914     // private
25915     isctype: function (character, type)
25916     {
25917         switch (type) {  
25918             case this.kCapitalLetter:
25919                 if (character >= 'A' && character <= 'Z') {
25920                     return true;
25921                 }
25922                 break;
25923             
25924             case this.kSmallLetter:
25925                 if (character >= 'a' && character <= 'z') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kDigit:
25931                 if (character >= '0' && character <= '9') {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             case this.kPunctuation:
25937                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25938                     return true;
25939                 }
25940                 break;
25941             
25942             default:
25943                 return false;
25944         }
25945
25946     },
25947     // private
25948     IsLongEnough: function (pwd, size)
25949     {
25950         return !(pwd == null || isNaN(size) || pwd.length < size);
25951     },
25952     // private
25953     SpansEnoughCharacterSets: function (word, nb)
25954     {
25955         if (!this.IsLongEnough(word, nb))
25956         {
25957             return false;
25958         }
25959
25960         var characterSetChecks = new Array(
25961             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25962             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25963         );
25964         
25965         for (var index = 0; index < word.length; ++index) {
25966             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25967                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25968                     characterSetChecks[nCharSet].fResult = true;
25969                     break;
25970                 }
25971             }
25972         }
25973
25974         var nCharSets = 0;
25975         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25976             if (characterSetChecks[nCharSet].fResult) {
25977                 ++nCharSets;
25978             }
25979         }
25980
25981         if (nCharSets < nb) {
25982             return false;
25983         }
25984         return true;
25985     },
25986     // private
25987     ClientSideStrongPassword: function (pwd)
25988     {
25989         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25990     },
25991     // private
25992     ClientSideMediumPassword: function (pwd)
25993     {
25994         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25995     },
25996     // private
25997     ClientSideWeakPassword: function (pwd)
25998     {
25999         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26000     }
26001           
26002 });
26003 Roo.htmleditor = {};
26004  
26005 /**
26006  * @class Roo.htmleditor.Filter
26007  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26008  * @cfg {DomElement} node The node to iterate and filter
26009  * @cfg {boolean|String|Array} tag Tags to replace 
26010  * @constructor
26011  * Create a new Filter.
26012  * @param {Object} config Configuration options
26013  */
26014
26015
26016
26017 Roo.htmleditor.Filter = function(cfg) {
26018     Roo.apply(this.cfg);
26019     // this does not actually call walk as it's really just a abstract class
26020 }
26021
26022
26023 Roo.htmleditor.Filter.prototype = {
26024     
26025     node: false,
26026     
26027     tag: false,
26028
26029     // overrride to do replace comments.
26030     replaceComment : false,
26031     
26032     // overrride to do replace or do stuff with tags..
26033     replaceTag : false,
26034     
26035     walk : function(dom)
26036     {
26037         Roo.each( Array.from(dom.childNodes), function( e ) {
26038             switch(true) {
26039                 
26040                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26041                     this.replaceComment(e);
26042                     return;
26043                 
26044                 case e.nodeType != 1: //not a node.
26045                     return;
26046                 
26047                 case this.tag === true: // everything
26048                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26049                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26050                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26051                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26052                     if (this.replaceTag && false === this.replaceTag(e)) {
26053                         return;
26054                     }
26055                     if (e.hasChildNodes()) {
26056                         this.walk(e);
26057                     }
26058                     return;
26059                 
26060                 default:    // tags .. that do not match.
26061                     if (e.hasChildNodes()) {
26062                         this.walk(e);
26063                     }
26064             }
26065             
26066         }, this);
26067         
26068     },
26069     
26070     
26071     removeNodeKeepChildren : function( node)
26072     {
26073     
26074         ar = Array.from(node.childNodes);
26075         for (var i = 0; i < ar.length; i++) {
26076          
26077             node.removeChild(ar[i]);
26078             // what if we need to walk these???
26079             node.parentNode.insertBefore(ar[i], node);
26080            
26081         }
26082         node.parentNode.removeChild(node);
26083     }
26084 }; 
26085
26086 /**
26087  * @class Roo.htmleditor.FilterAttributes
26088  * clean attributes and  styles including http:// etc.. in attribute
26089  * @constructor
26090 * Run a new Attribute Filter
26091 * @param {Object} config Configuration options
26092  */
26093 Roo.htmleditor.FilterAttributes = function(cfg)
26094 {
26095     Roo.apply(this, cfg);
26096     this.attrib_black = this.attrib_black || [];
26097     this.attrib_white = this.attrib_white || [];
26098
26099     this.attrib_clean = this.attrib_clean || [];
26100     this.style_white = this.style_white || [];
26101     this.style_black = this.style_black || [];
26102     this.walk(cfg.node);
26103 }
26104
26105 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26106 {
26107     tag: true, // all tags
26108     
26109     attrib_black : false, // array
26110     attrib_clean : false,
26111     attrib_white : false,
26112
26113     style_white : false,
26114     style_black : false,
26115      
26116      
26117     replaceTag : function(node)
26118     {
26119         if (!node.attributes || !node.attributes.length) {
26120             return true;
26121         }
26122         
26123         for (var i = node.attributes.length-1; i > -1 ; i--) {
26124             var a = node.attributes[i];
26125             //console.log(a);
26126             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26127                 node.removeAttribute(a.name);
26128                 continue;
26129             }
26130             
26131             
26132             
26133             if (a.name.toLowerCase().substr(0,2)=='on')  {
26134                 node.removeAttribute(a.name);
26135                 continue;
26136             }
26137             
26138             
26139             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26140                 node.removeAttribute(a.name);
26141                 continue;
26142             }
26143             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26144                 this.cleanAttr(node,a.name,a.value); // fixme..
26145                 continue;
26146             }
26147             if (a.name == 'style') {
26148                 this.cleanStyle(node,a.name,a.value);
26149                 continue;
26150             }
26151             /// clean up MS crap..
26152             // tecnically this should be a list of valid class'es..
26153             
26154             
26155             if (a.name == 'class') {
26156                 if (a.value.match(/^Mso/)) {
26157                     node.removeAttribute('class');
26158                 }
26159                 
26160                 if (a.value.match(/^body$/)) {
26161                     node.removeAttribute('class');
26162                 }
26163                 continue;
26164             }
26165             
26166             
26167             // style cleanup!?
26168             // class cleanup?
26169             
26170         }
26171         return true; // clean children
26172     },
26173         
26174     cleanAttr: function(node, n,v)
26175     {
26176         
26177         if (v.match(/^\./) || v.match(/^\//)) {
26178             return;
26179         }
26180         if (v.match(/^(http|https):\/\//)
26181             || v.match(/^mailto:/) 
26182             || v.match(/^ftp:/)
26183             || v.match(/^data:/)
26184             ) {
26185             return;
26186         }
26187         if (v.match(/^#/)) {
26188             return;
26189         }
26190         if (v.match(/^\{/)) { // allow template editing.
26191             return;
26192         }
26193 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26194         node.removeAttribute(n);
26195         
26196     },
26197     cleanStyle : function(node,  n,v)
26198     {
26199         if (v.match(/expression/)) { //XSS?? should we even bother..
26200             node.removeAttribute(n);
26201             return;
26202         }
26203         
26204         var parts = v.split(/;/);
26205         var clean = [];
26206         
26207         Roo.each(parts, function(p) {
26208             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26209             if (!p.length) {
26210                 return true;
26211             }
26212             var l = p.split(':').shift().replace(/\s+/g,'');
26213             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26214             
26215             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26216                 return true;
26217             }
26218             //Roo.log()
26219             // only allow 'c whitelisted system attributes'
26220             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26221                 return true;
26222             }
26223             
26224             
26225             clean.push(p);
26226             return true;
26227         },this);
26228         if (clean.length) { 
26229             node.setAttribute(n, clean.join(';'));
26230         } else {
26231             node.removeAttribute(n);
26232         }
26233         
26234     }
26235         
26236         
26237         
26238     
26239 });/**
26240  * @class Roo.htmleditor.FilterBlack
26241  * remove blacklisted elements.
26242  * @constructor
26243  * Run a new Blacklisted Filter
26244  * @param {Object} config Configuration options
26245  */
26246
26247 Roo.htmleditor.FilterBlack = function(cfg)
26248 {
26249     Roo.apply(this, cfg);
26250     this.walk(cfg.node);
26251 }
26252
26253 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26254 {
26255     tag : true, // all elements.
26256    
26257     replaceTag : function(n)
26258     {
26259         n.parentNode.removeChild(n);
26260     }
26261 });
26262 /**
26263  * @class Roo.htmleditor.FilterComment
26264  * remove comments.
26265  * @constructor
26266 * Run a new Comments Filter
26267 * @param {Object} config Configuration options
26268  */
26269 Roo.htmleditor.FilterComment = function(cfg)
26270 {
26271     this.walk(cfg.node);
26272 }
26273
26274 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26275 {
26276   
26277     replaceComment : function(n)
26278     {
26279         n.parentNode.removeChild(n);
26280     }
26281 });/**
26282  * @class Roo.htmleditor.FilterKeepChildren
26283  * remove tags but keep children
26284  * @constructor
26285  * Run a new Keep Children Filter
26286  * @param {Object} config Configuration options
26287  */
26288
26289 Roo.htmleditor.FilterKeepChildren = function(cfg)
26290 {
26291     Roo.apply(this, cfg);
26292     if (this.tag === false) {
26293         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26294     }
26295     // hacky?
26296     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26297         this.cleanNamespace = true;
26298     }
26299         
26300     this.walk(cfg.node);
26301 }
26302
26303 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26304 {
26305     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26306   
26307     replaceTag : function(node)
26308     {
26309         // walk children...
26310         //Roo.log(node.tagName);
26311         var ar = Array.from(node.childNodes);
26312         //remove first..
26313         
26314         for (var i = 0; i < ar.length; i++) {
26315             var e = ar[i];
26316             if (e.nodeType == 1) {
26317                 if (
26318                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26319                     || // array and it matches
26320                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26321                     ||
26322                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26323                     ||
26324                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26325                 ) {
26326                     this.replaceTag(ar[i]); // child is blacklisted as well...
26327                     continue;
26328                 }
26329             }
26330         }  
26331         ar = Array.from(node.childNodes);
26332         for (var i = 0; i < ar.length; i++) {
26333          
26334             node.removeChild(ar[i]);
26335             // what if we need to walk these???
26336             node.parentNode.insertBefore(ar[i], node);
26337             if (this.tag !== false) {
26338                 this.walk(ar[i]);
26339                 
26340             }
26341         }
26342         //Roo.log("REMOVE:" + node.tagName);
26343         node.parentNode.removeChild(node);
26344         return false; // don't walk children
26345         
26346         
26347     }
26348 });/**
26349  * @class Roo.htmleditor.FilterParagraph
26350  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26351  * like on 'push' to remove the <p> tags and replace them with line breaks.
26352  * @constructor
26353  * Run a new Paragraph Filter
26354  * @param {Object} config Configuration options
26355  */
26356
26357 Roo.htmleditor.FilterParagraph = function(cfg)
26358 {
26359     // no need to apply config.
26360     this.walk(cfg.node);
26361 }
26362
26363 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26364 {
26365     
26366      
26367     tag : 'P',
26368     
26369      
26370     replaceTag : function(node)
26371     {
26372         
26373         if (node.childNodes.length == 1 &&
26374             node.childNodes[0].nodeType == 3 &&
26375             node.childNodes[0].textContent.trim().length < 1
26376             ) {
26377             // remove and replace with '<BR>';
26378             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26379             return false; // no need to walk..
26380         }
26381         var ar = Array.from(node.childNodes);
26382         for (var i = 0; i < ar.length; i++) {
26383             node.removeChild(ar[i]);
26384             // what if we need to walk these???
26385             node.parentNode.insertBefore(ar[i], node);
26386         }
26387         // now what about this?
26388         // <p> &nbsp; </p>
26389         
26390         // double BR.
26391         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26392         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26393         node.parentNode.removeChild(node);
26394         
26395         return false;
26396
26397     }
26398     
26399 });/**
26400  * @class Roo.htmleditor.FilterSpan
26401  * filter span's with no attributes out..
26402  * @constructor
26403  * Run a new Span Filter
26404  * @param {Object} config Configuration options
26405  */
26406
26407 Roo.htmleditor.FilterSpan = function(cfg)
26408 {
26409     // no need to apply config.
26410     this.walk(cfg.node);
26411 }
26412
26413 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26414 {
26415      
26416     tag : 'SPAN',
26417      
26418  
26419     replaceTag : function(node)
26420     {
26421         if (node.attributes && node.attributes.length > 0) {
26422             return true; // walk if there are any.
26423         }
26424         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26425         return false;
26426      
26427     }
26428     
26429 });/**
26430  * @class Roo.htmleditor.FilterTableWidth
26431   try and remove table width data - as that frequently messes up other stuff.
26432  * 
26433  *      was cleanTableWidths.
26434  *
26435  * Quite often pasting from word etc.. results in tables with column and widths.
26436  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26437  *
26438  * @constructor
26439  * Run a new Table Filter
26440  * @param {Object} config Configuration options
26441  */
26442
26443 Roo.htmleditor.FilterTableWidth = function(cfg)
26444 {
26445     // no need to apply config.
26446     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26447     this.walk(cfg.node);
26448 }
26449
26450 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26451 {
26452      
26453      
26454     
26455     replaceTag: function(node) {
26456         
26457         
26458       
26459         if (node.hasAttribute('width')) {
26460             node.removeAttribute('width');
26461         }
26462         
26463          
26464         if (node.hasAttribute("style")) {
26465             // pretty basic...
26466             
26467             var styles = node.getAttribute("style").split(";");
26468             var nstyle = [];
26469             Roo.each(styles, function(s) {
26470                 if (!s.match(/:/)) {
26471                     return;
26472                 }
26473                 var kv = s.split(":");
26474                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26475                     return;
26476                 }
26477                 // what ever is left... we allow.
26478                 nstyle.push(s);
26479             });
26480             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26481             if (!nstyle.length) {
26482                 node.removeAttribute('style');
26483             }
26484         }
26485         
26486         return true; // continue doing children..
26487     }
26488 });/**
26489  * @class Roo.htmleditor.FilterWord
26490  * try and clean up all the mess that Word generates.
26491  * 
26492  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26493  
26494  * @constructor
26495  * Run a new Span Filter
26496  * @param {Object} config Configuration options
26497  */
26498
26499 Roo.htmleditor.FilterWord = function(cfg)
26500 {
26501     // no need to apply config.
26502     this.replaceDocBullets(cfg.node);
26503     
26504     this.replaceAname(cfg.node);
26505     // this is disabled as the removal is done by other filters;
26506    // this.walk(cfg.node);
26507     
26508     
26509 }
26510
26511 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26512 {
26513     tag: true,
26514      
26515     
26516     /**
26517      * Clean up MS wordisms...
26518      */
26519     replaceTag : function(node)
26520     {
26521          
26522         // no idea what this does - span with text, replaceds with just text.
26523         if(
26524                 node.nodeName == 'SPAN' &&
26525                 !node.hasAttributes() &&
26526                 node.childNodes.length == 1 &&
26527                 node.firstChild.nodeName == "#text"  
26528         ) {
26529             var textNode = node.firstChild;
26530             node.removeChild(textNode);
26531             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26532                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26533             }
26534             node.parentNode.insertBefore(textNode, node);
26535             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26536                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26537             }
26538             
26539             node.parentNode.removeChild(node);
26540             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26541         }
26542         
26543    
26544         
26545         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26546             node.parentNode.removeChild(node);
26547             return false; // dont do chidlren
26548         }
26549         //Roo.log(node.tagName);
26550         // remove - but keep children..
26551         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26552             //Roo.log('-- removed');
26553             while (node.childNodes.length) {
26554                 var cn = node.childNodes[0];
26555                 node.removeChild(cn);
26556                 node.parentNode.insertBefore(cn, node);
26557                 // move node to parent - and clean it..
26558                 if (cn.nodeType == 1) {
26559                     this.replaceTag(cn);
26560                 }
26561                 
26562             }
26563             node.parentNode.removeChild(node);
26564             /// no need to iterate chidlren = it's got none..
26565             //this.iterateChildren(node, this.cleanWord);
26566             return false; // no need to iterate children.
26567         }
26568         // clean styles
26569         if (node.className.length) {
26570             
26571             var cn = node.className.split(/\W+/);
26572             var cna = [];
26573             Roo.each(cn, function(cls) {
26574                 if (cls.match(/Mso[a-zA-Z]+/)) {
26575                     return;
26576                 }
26577                 cna.push(cls);
26578             });
26579             node.className = cna.length ? cna.join(' ') : '';
26580             if (!cna.length) {
26581                 node.removeAttribute("class");
26582             }
26583         }
26584         
26585         if (node.hasAttribute("lang")) {
26586             node.removeAttribute("lang");
26587         }
26588         
26589         if (node.hasAttribute("style")) {
26590             
26591             var styles = node.getAttribute("style").split(";");
26592             var nstyle = [];
26593             Roo.each(styles, function(s) {
26594                 if (!s.match(/:/)) {
26595                     return;
26596                 }
26597                 var kv = s.split(":");
26598                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26599                     return;
26600                 }
26601                 // what ever is left... we allow.
26602                 nstyle.push(s);
26603             });
26604             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26605             if (!nstyle.length) {
26606                 node.removeAttribute('style');
26607             }
26608         }
26609         return true; // do children
26610         
26611         
26612         
26613     },
26614     
26615     styleToObject: function(node)
26616     {
26617         var styles = (node.getAttribute("style") || '').split(";");
26618         var ret = {};
26619         Roo.each(styles, function(s) {
26620             if (!s.match(/:/)) {
26621                 return;
26622             }
26623             var kv = s.split(":");
26624              
26625             // what ever is left... we allow.
26626             ret[kv[0].trim()] = kv[1];
26627         });
26628         return ret;
26629     },
26630     
26631     
26632     replaceAname : function (doc)
26633     {
26634         // replace all the a/name without..
26635         var aa = Array.from(doc.getElementsByTagName('a'));
26636         for (var i = 0; i  < aa.length; i++) {
26637             var a = aa[i];
26638             if (a.hasAttribute("name")) {
26639                 a.removeAttribute("name");
26640             }
26641             if (a.hasAttribute("href")) {
26642                 continue;
26643             }
26644             // reparent children.
26645             this.removeNodeKeepChildren(a);
26646             
26647         }
26648         
26649         
26650         
26651     },
26652
26653     
26654     
26655     replaceDocBullets : function(doc)
26656     {
26657         // this is a bit odd - but it appears some indents use ql-indent-1
26658          //Roo.log(doc.innerHTML);
26659         
26660         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26661         for( var i = 0; i < listpara.length; i ++) {
26662             listpara[i].className = "MsoListParagraph";
26663         }
26664         
26665         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26666         for( var i = 0; i < listpara.length; i ++) {
26667             listpara[i].className = "MsoListParagraph";
26668         }
26669         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26670         for( var i = 0; i < listpara.length; i ++) {
26671             listpara[i].className = "MsoListParagraph";
26672         }
26673         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26674         for( var i = 0; i < listpara.length; i ++) {
26675             listpara[i].className = "MsoListParagraph";
26676         }
26677         
26678         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26679         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26680         for( var i = 0; i < htwo.length; i ++) {
26681             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26682                 htwo[i].className = "MsoListParagraph";
26683             }
26684         }
26685         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26686         for( var i = 0; i < listpara.length; i ++) {
26687             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26688                 listpara[i].className = "MsoListParagraph";
26689             } else {
26690                 listpara[i].className = "MsoNormalx";
26691             }
26692         }
26693        
26694         listpara = doc.getElementsByClassName('MsoListParagraph');
26695         // Roo.log(doc.innerHTML);
26696         
26697         
26698         
26699         while(listpara.length) {
26700             
26701             this.replaceDocBullet(listpara.item(0));
26702         }
26703       
26704     },
26705     
26706      
26707     
26708     replaceDocBullet : function(p)
26709     {
26710         // gather all the siblings.
26711         var ns = p,
26712             parent = p.parentNode,
26713             doc = parent.ownerDocument,
26714             items = [];
26715             
26716         var listtype = 'ul';   
26717         while (ns) {
26718             if (ns.nodeType != 1) {
26719                 ns = ns.nextSibling;
26720                 continue;
26721             }
26722             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26723                 break;
26724             }
26725             var spans = ns.getElementsByTagName('span');
26726             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26727                 items.push(ns);
26728                 ns = ns.nextSibling;
26729                 has_list = true;
26730                 if (spans.length && spans[0].hasAttribute('style')) {
26731                     var  style = this.styleToObject(spans[0]);
26732                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26733                         listtype = 'ol';
26734                     }
26735                 }
26736                 
26737                 continue;
26738             }
26739             var spans = ns.getElementsByTagName('span');
26740             if (!spans.length) {
26741                 break;
26742             }
26743             var has_list  = false;
26744             for(var i = 0; i < spans.length; i++) {
26745                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26746                     has_list = true;
26747                     break;
26748                 }
26749             }
26750             if (!has_list) {
26751                 break;
26752             }
26753             items.push(ns);
26754             ns = ns.nextSibling;
26755             
26756             
26757         }
26758         if (!items.length) {
26759             ns.className = "";
26760             return;
26761         }
26762         
26763         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26764         parent.insertBefore(ul, p);
26765         var lvl = 0;
26766         var stack = [ ul ];
26767         var last_li = false;
26768         
26769         var margin_to_depth = {};
26770         max_margins = -1;
26771         
26772         items.forEach(function(n, ipos) {
26773             //Roo.log("got innertHMLT=" + n.innerHTML);
26774             
26775             var spans = n.getElementsByTagName('span');
26776             if (!spans.length) {
26777                 //Roo.log("No spans found");
26778                  
26779                 parent.removeChild(n);
26780                 
26781                 
26782                 return; // skip it...
26783             }
26784            
26785                 
26786             var num = 1;
26787             var style = {};
26788             for(var i = 0; i < spans.length; i++) {
26789             
26790                 style = this.styleToObject(spans[i]);
26791                 if (typeof(style['mso-list']) == 'undefined') {
26792                     continue;
26793                 }
26794                 if (listtype == 'ol') {
26795                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26796                 }
26797                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26798                 break;
26799             }
26800             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26801             style = this.styleToObject(n); // mo-list is from the parent node.
26802             if (typeof(style['mso-list']) == 'undefined') {
26803                 //Roo.log("parent is missing level");
26804                   
26805                 parent.removeChild(n);
26806                  
26807                 return;
26808             }
26809             
26810             var margin = style['margin-left'];
26811             if (typeof(margin_to_depth[margin]) == 'undefined') {
26812                 max_margins++;
26813                 margin_to_depth[margin] = max_margins;
26814             }
26815             nlvl = margin_to_depth[margin] ;
26816              
26817             if (nlvl > lvl) {
26818                 //new indent
26819                 var nul = doc.createElement(listtype); // what about number lists...
26820                 if (!last_li) {
26821                     last_li = doc.createElement('li');
26822                     stack[lvl].appendChild(last_li);
26823                 }
26824                 last_li.appendChild(nul);
26825                 stack[nlvl] = nul;
26826                 
26827             }
26828             lvl = nlvl;
26829             
26830             // not starting at 1..
26831             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26832                 stack[nlvl].setAttribute("start", num);
26833             }
26834             
26835             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26836             last_li = nli;
26837             nli.innerHTML = n.innerHTML;
26838             //Roo.log("innerHTML = " + n.innerHTML);
26839             parent.removeChild(n);
26840             
26841              
26842              
26843             
26844         },this);
26845         
26846         
26847         
26848         
26849     }
26850     
26851     
26852     
26853 });
26854 /**
26855  * @class Roo.htmleditor.FilterStyleToTag
26856  * part of the word stuff... - certain 'styles' should be converted to tags.
26857  * eg.
26858  *   font-weight: bold -> bold
26859  *   ?? super / subscrit etc..
26860  * 
26861  * @constructor
26862 * Run a new style to tag filter.
26863 * @param {Object} config Configuration options
26864  */
26865 Roo.htmleditor.FilterStyleToTag = function(cfg)
26866 {
26867     
26868     this.tags = {
26869         B  : [ 'fontWeight' , 'bold'],
26870         I :  [ 'fontStyle' , 'italic'],
26871         //pre :  [ 'font-style' , 'italic'],
26872         // h1.. h6 ?? font-size?
26873         SUP : [ 'verticalAlign' , 'super' ],
26874         SUB : [ 'verticalAlign' , 'sub' ]
26875         
26876         
26877     };
26878     
26879     Roo.apply(this, cfg);
26880      
26881     
26882     this.walk(cfg.node);
26883     
26884     
26885     
26886 }
26887
26888
26889 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26890 {
26891     tag: true, // all tags
26892     
26893     tags : false,
26894     
26895     
26896     replaceTag : function(node)
26897     {
26898         
26899         
26900         if (node.getAttribute("style") === null) {
26901             return true;
26902         }
26903         var inject = [];
26904         for (var k in this.tags) {
26905             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26906                 inject.push(k);
26907                 node.style.removeProperty(this.tags[k][0]);
26908             }
26909         }
26910         if (!inject.length) {
26911             return true; 
26912         }
26913         var cn = Array.from(node.childNodes);
26914         var nn = node;
26915         Roo.each(inject, function(t) {
26916             var nc = node.ownerDocument.createElement(t);
26917             nn.appendChild(nc);
26918             nn = nc;
26919         });
26920         for(var i = 0;i < cn.length;cn++) {
26921             node.removeChild(cn[i]);
26922             nn.appendChild(cn[i]);
26923         }
26924         return true /// iterate thru
26925     }
26926     
26927 })/**
26928  * @class Roo.htmleditor.FilterLongBr
26929  * BR/BR/BR - keep a maximum of 2...
26930  * @constructor
26931  * Run a new Long BR Filter
26932  * @param {Object} config Configuration options
26933  */
26934
26935 Roo.htmleditor.FilterLongBr = function(cfg)
26936 {
26937     // no need to apply config.
26938     this.walk(cfg.node);
26939 }
26940
26941 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26942 {
26943     
26944      
26945     tag : 'BR',
26946     
26947      
26948     replaceTag : function(node)
26949     {
26950         
26951         var ps = node.nextSibling;
26952         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26953             ps = ps.nextSibling;
26954         }
26955         
26956         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26957             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26958             return false;
26959         }
26960         
26961         if (!ps || ps.nodeType != 1) {
26962             return false;
26963         }
26964         
26965         if (!ps || ps.tagName != 'BR') {
26966            
26967             return false;
26968         }
26969         
26970         
26971         
26972         
26973         
26974         if (!node.previousSibling) {
26975             return false;
26976         }
26977         var ps = node.previousSibling;
26978         
26979         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26980             ps = ps.previousSibling;
26981         }
26982         if (!ps || ps.nodeType != 1) {
26983             return false;
26984         }
26985         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26986         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26987             return false;
26988         }
26989         
26990         node.parentNode.removeChild(node); // remove me...
26991         
26992         return false; // no need to do children
26993
26994     }
26995     
26996 }); 
26997
26998 /**
26999  * @class Roo.htmleditor.FilterBlock
27000  * removes id / data-block and contenteditable that are associated with blocks
27001  * usage should be done on a cloned copy of the dom
27002  * @constructor
27003 * Run a new Attribute Filter { node : xxxx }}
27004 * @param {Object} config Configuration options
27005  */
27006 Roo.htmleditor.FilterBlock = function(cfg)
27007 {
27008     Roo.apply(this, cfg);
27009     var qa = cfg.node.querySelectorAll;
27010     this.removeAttributes('data-block');
27011     this.removeAttributes('contenteditable');
27012     this.removeAttributes('id');
27013     
27014 }
27015
27016 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27017 {
27018     node: true, // all tags
27019      
27020      
27021     removeAttributes : function(attr)
27022     {
27023         var ar = this.node.querySelectorAll('*[' + attr + ']');
27024         for (var i =0;i<ar.length;i++) {
27025             ar[i].removeAttribute(attr);
27026         }
27027     }
27028         
27029         
27030         
27031     
27032 });
27033 /**
27034  * @class Roo.htmleditor.KeyEnter
27035  * Handle Enter press..
27036  * @cfg {Roo.HtmlEditorCore} core the editor.
27037  * @constructor
27038  * Create a new Filter.
27039  * @param {Object} config Configuration options
27040  */
27041
27042
27043
27044
27045
27046 Roo.htmleditor.KeyEnter = function(cfg) {
27047     Roo.apply(this, cfg);
27048     // this does not actually call walk as it's really just a abstract class
27049  
27050     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
27051 }
27052
27053 //Roo.htmleditor.KeyEnter.i = 0;
27054
27055
27056 Roo.htmleditor.KeyEnter.prototype = {
27057     
27058     core : false,
27059     
27060     keypress : function(e)
27061     {
27062         if (e.charCode != 13 && e.charCode != 10) {
27063             Roo.log([e.charCode,e]);
27064             return true;
27065         }
27066         e.preventDefault();
27067         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
27068         var doc = this.core.doc;
27069           //add a new line
27070        
27071     
27072         var sel = this.core.getSelection();
27073         var range = sel.getRangeAt(0);
27074         var n = range.commonAncestorContainer;
27075         var pc = range.closest([ 'ol', 'ul']);
27076         var pli = range.closest('li');
27077         if (!pc || e.ctrlKey) {
27078             // on it list, or ctrl pressed.
27079             if (!e.ctrlKey) {
27080                 sel.insertNode('br', 'after'); 
27081             } else {
27082                 // only do this if we have ctrl key..
27083                 var br = doc.createElement('br');
27084                 br.className = 'clear';
27085                 br.setAttribute('style', 'clear: both');
27086                 sel.insertNode(br, 'after'); 
27087             }
27088             
27089          
27090             this.core.undoManager.addEvent();
27091             this.core.fireEditorEvent(e);
27092             return false;
27093         }
27094         
27095         // deal with <li> insetion
27096         if (pli.innerText.trim() == '' &&
27097             pli.previousSibling &&
27098             pli.previousSibling.nodeName == 'LI' &&
27099             pli.previousSibling.innerText.trim() ==  '') {
27100             pli.parentNode.removeChild(pli.previousSibling);
27101             sel.cursorAfter(pc);
27102             this.core.undoManager.addEvent();
27103             this.core.fireEditorEvent(e);
27104             return false;
27105         }
27106     
27107         var li = doc.createElement('LI');
27108         li.innerHTML = '&nbsp;';
27109         if (!pli || !pli.firstSibling) {
27110             pc.appendChild(li);
27111         } else {
27112             pli.parentNode.insertBefore(li, pli.firstSibling);
27113         }
27114         sel.cursorText (li.firstChild);
27115       
27116         this.core.undoManager.addEvent();
27117         this.core.fireEditorEvent(e);
27118
27119         return false;
27120         
27121     
27122         
27123         
27124          
27125     }
27126 };
27127      
27128 /**
27129  * @class Roo.htmleditor.Block
27130  * Base class for html editor blocks - do not use it directly .. extend it..
27131  * @cfg {DomElement} node The node to apply stuff to.
27132  * @cfg {String} friendly_name the name that appears in the context bar about this block
27133  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
27134  
27135  * @constructor
27136  * Create a new Filter.
27137  * @param {Object} config Configuration options
27138  */
27139
27140 Roo.htmleditor.Block  = function(cfg)
27141 {
27142     // do nothing .. should not be called really.
27143 }
27144 /**
27145  * factory method to get the block from an element (using cache if necessary)
27146  * @static
27147  * @param {HtmlElement} the dom element
27148  */
27149 Roo.htmleditor.Block.factory = function(node)
27150 {
27151     var cc = Roo.htmleditor.Block.cache;
27152     var id = Roo.get(node).id;
27153     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
27154         Roo.htmleditor.Block.cache[id].readElement(node);
27155         return Roo.htmleditor.Block.cache[id];
27156     }
27157     var db  = node.getAttribute('data-block');
27158     if (!db) {
27159         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27160     }
27161     var cls = Roo.htmleditor['Block' + db];
27162     if (typeof(cls) == 'undefined') {
27163         //Roo.log(node.getAttribute('data-block'));
27164         Roo.log("OOps missing block : " + 'Block' + db);
27165         return false;
27166     }
27167     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27168     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27169 };
27170
27171 /**
27172  * initalize all Elements from content that are 'blockable'
27173  * @static
27174  * @param the body element
27175  */
27176 Roo.htmleditor.Block.initAll = function(body, type)
27177 {
27178     if (typeof(type) == 'undefined') {
27179         var ia = Roo.htmleditor.Block.initAll;
27180         ia(body,'table');
27181         ia(body,'td');
27182         ia(body,'figure');
27183         return;
27184     }
27185     Roo.each(Roo.get(body).query(type), function(e) {
27186         Roo.htmleditor.Block.factory(e);    
27187     },this);
27188 };
27189 // question goes here... do we need to clear out this cache sometimes?
27190 // or show we make it relivant to the htmleditor.
27191 Roo.htmleditor.Block.cache = {};
27192
27193 Roo.htmleditor.Block.prototype = {
27194     
27195     node : false,
27196     
27197      // used by context menu
27198     friendly_name : 'Based Block',
27199     
27200     // text for button to delete this element
27201     deleteTitle : false,
27202     
27203     context : false,
27204     /**
27205      * Update a node with values from this object
27206      * @param {DomElement} node
27207      */
27208     updateElement : function(node)
27209     {
27210         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27211     },
27212      /**
27213      * convert to plain HTML for calling insertAtCursor..
27214      */
27215     toHTML : function()
27216     {
27217         return Roo.DomHelper.markup(this.toObject());
27218     },
27219     /**
27220      * used by readEleemnt to extract data from a node
27221      * may need improving as it's pretty basic
27222      
27223      * @param {DomElement} node
27224      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27225      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27226      * @param {String} style the style property - eg. text-align
27227      */
27228     getVal : function(node, tag, attr, style)
27229     {
27230         var n = node;
27231         if (tag !== true && n.tagName != tag.toUpperCase()) {
27232             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27233             // but kiss for now.
27234             n = node.getElementsByTagName(tag).item(0);
27235         }
27236         if (!n) {
27237             return '';
27238         }
27239         if (attr === false) {
27240             return n;
27241         }
27242         if (attr == 'html') {
27243             return n.innerHTML;
27244         }
27245         if (attr == 'style') {
27246             return n.style[style]; 
27247         }
27248         
27249         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27250             
27251     },
27252     /**
27253      * create a DomHelper friendly object - for use with 
27254      * Roo.DomHelper.markup / overwrite / etc..
27255      * (override this)
27256      */
27257     toObject : function()
27258     {
27259         return {};
27260     },
27261       /**
27262      * Read a node that has a 'data-block' property - and extract the values from it.
27263      * @param {DomElement} node - the node
27264      */
27265     readElement : function(node)
27266     {
27267         
27268     } 
27269     
27270     
27271 };
27272
27273  
27274
27275 /**
27276  * @class Roo.htmleditor.BlockFigure
27277  * Block that has an image and a figcaption
27278  * @cfg {String} image_src the url for the image
27279  * @cfg {String} align (left|right) alignment for the block default left
27280  * @cfg {String} caption the text to appear below  (and in the alt tag)
27281  * @cfg {String} caption_display (block|none) display or not the caption
27282  * @cfg {String|number} image_width the width of the image number or %?
27283  * @cfg {String|number} image_height the height of the image number or %?
27284  * 
27285  * @constructor
27286  * Create a new Filter.
27287  * @param {Object} config Configuration options
27288  */
27289
27290 Roo.htmleditor.BlockFigure = function(cfg)
27291 {
27292     if (cfg.node) {
27293         this.readElement(cfg.node);
27294         this.updateElement(cfg.node);
27295     }
27296     Roo.apply(this, cfg);
27297 }
27298 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27299  
27300     
27301     // setable values.
27302     image_src: '',
27303     align: 'center',
27304     caption : '',
27305     caption_display : 'block',
27306     width : '100%',
27307     cls : '',
27308     href: '',
27309     video_url : '',
27310     
27311     // margin: '2%', not used
27312     
27313     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27314
27315     
27316     // used by context menu
27317     friendly_name : 'Image with caption',
27318     deleteTitle : "Delete Image and Caption",
27319     
27320     contextMenu : function(toolbar)
27321     {
27322         
27323         var block = function() {
27324             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27325         };
27326         
27327         
27328         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27329         
27330         var syncValue = toolbar.editorcore.syncValue;
27331         
27332         var fields = {};
27333         
27334         return [
27335              {
27336                 xtype : 'TextItem',
27337                 text : "Source: ",
27338                 xns : rooui.Toolbar  //Boostrap?
27339             },
27340             {
27341                 xtype : 'Button',
27342                 text: 'Change Image URL',
27343                  
27344                 listeners : {
27345                     click: function (btn, state)
27346                     {
27347                         var b = block();
27348                         
27349                         Roo.MessageBox.show({
27350                             title : "Image Source URL",
27351                             msg : "Enter the url for the image",
27352                             buttons: Roo.MessageBox.OKCANCEL,
27353                             fn: function(btn, val){
27354                                 if (btn != 'ok') {
27355                                     return;
27356                                 }
27357                                 b.image_src = val;
27358                                 b.updateElement();
27359                                 syncValue();
27360                                 toolbar.editorcore.onEditorEvent();
27361                             },
27362                             minWidth:250,
27363                             prompt:true,
27364                             //multiline: multiline,
27365                             modal : true,
27366                             value : b.image_src
27367                         });
27368                     }
27369                 },
27370                 xns : rooui.Toolbar
27371             },
27372          
27373             {
27374                 xtype : 'Button',
27375                 text: 'Change Link URL',
27376                  
27377                 listeners : {
27378                     click: function (btn, state)
27379                     {
27380                         var b = block();
27381                         
27382                         Roo.MessageBox.show({
27383                             title : "Link URL",
27384                             msg : "Enter the url for the link - leave blank to have no link",
27385                             buttons: Roo.MessageBox.OKCANCEL,
27386                             fn: function(btn, val){
27387                                 if (btn != 'ok') {
27388                                     return;
27389                                 }
27390                                 b.href = val;
27391                                 b.updateElement();
27392                                 syncValue();
27393                                 toolbar.editorcore.onEditorEvent();
27394                             },
27395                             minWidth:250,
27396                             prompt:true,
27397                             //multiline: multiline,
27398                             modal : true,
27399                             value : b.href
27400                         });
27401                     }
27402                 },
27403                 xns : rooui.Toolbar
27404             },
27405             {
27406                 xtype : 'Button',
27407                 text: 'Show Video URL',
27408                  
27409                 listeners : {
27410                     click: function (btn, state)
27411                     {
27412                         Roo.MessageBox.alert("Video URL",
27413                             block().video_url == '' ? 'This image is not linked ot a video' :
27414                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27415                     }
27416                 },
27417                 xns : rooui.Toolbar
27418             },
27419             
27420             
27421             {
27422                 xtype : 'TextItem',
27423                 text : "Width: ",
27424                 xns : rooui.Toolbar  //Boostrap?
27425             },
27426             {
27427                 xtype : 'ComboBox',
27428                 allowBlank : false,
27429                 displayField : 'val',
27430                 editable : true,
27431                 listWidth : 100,
27432                 triggerAction : 'all',
27433                 typeAhead : true,
27434                 valueField : 'val',
27435                 width : 70,
27436                 name : 'width',
27437                 listeners : {
27438                     select : function (combo, r, index)
27439                     {
27440                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27441                         var b = block();
27442                         b.width = r.get('val');
27443                         b.updateElement();
27444                         syncValue();
27445                         toolbar.editorcore.onEditorEvent();
27446                     }
27447                 },
27448                 xns : rooui.form,
27449                 store : {
27450                     xtype : 'SimpleStore',
27451                     data : [
27452                         ['100%'],
27453                         ['80%'],
27454                         ['50%'],
27455                         ['20%'],
27456                         ['10%']
27457                     ],
27458                     fields : [ 'val'],
27459                     xns : Roo.data
27460                 }
27461             },
27462             {
27463                 xtype : 'TextItem',
27464                 text : "Align: ",
27465                 xns : rooui.Toolbar  //Boostrap?
27466             },
27467             {
27468                 xtype : 'ComboBox',
27469                 allowBlank : false,
27470                 displayField : 'val',
27471                 editable : true,
27472                 listWidth : 100,
27473                 triggerAction : 'all',
27474                 typeAhead : true,
27475                 valueField : 'val',
27476                 width : 70,
27477                 name : 'align',
27478                 listeners : {
27479                     select : function (combo, r, index)
27480                     {
27481                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27482                         var b = block();
27483                         b.align = r.get('val');
27484                         b.updateElement();
27485                         syncValue();
27486                         toolbar.editorcore.onEditorEvent();
27487                     }
27488                 },
27489                 xns : rooui.form,
27490                 store : {
27491                     xtype : 'SimpleStore',
27492                     data : [
27493                         ['left'],
27494                         ['right'],
27495                         ['center']
27496                     ],
27497                     fields : [ 'val'],
27498                     xns : Roo.data
27499                 }
27500             },
27501             
27502             
27503             {
27504                 xtype : 'Button',
27505                 text: 'Hide Caption',
27506                 name : 'caption_display',
27507                 pressed : false,
27508                 enableToggle : true,
27509                 setValue : function(v) {
27510                     // this trigger toggle.
27511                      
27512                     this.setText(v ? "Hide Caption" : "Show Caption");
27513                     this.setPressed(v != 'block');
27514                 },
27515                 listeners : {
27516                     toggle: function (btn, state)
27517                     {
27518                         var b  = block();
27519                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27520                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27521                         b.updateElement();
27522                         syncValue();
27523                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27524                         toolbar.editorcore.onEditorEvent();
27525                     }
27526                 },
27527                 xns : rooui.Toolbar
27528             }
27529         ];
27530         
27531     },
27532     /**
27533      * create a DomHelper friendly object - for use with
27534      * Roo.DomHelper.markup / overwrite / etc..
27535      */
27536     toObject : function()
27537     {
27538         var d = document.createElement('div');
27539         d.innerHTML = this.caption;
27540         
27541         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27542         
27543         var iw = this.align == 'center' ? this.width : '100%';
27544         var img =   {
27545             tag : 'img',
27546             contenteditable : 'false',
27547             src : this.image_src,
27548             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27549             style: {
27550                 width : iw,
27551                 maxWidth : iw + ' !important', // this is not getting rendered?
27552                 margin : m  
27553                 
27554             }
27555         };
27556         /*
27557         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27558                     '<a href="{2}">' + 
27559                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27560                     '</a>' + 
27561                 '</div>',
27562         */
27563                 
27564         if (this.href.length > 0) {
27565             img = {
27566                 tag : 'a',
27567                 href: this.href,
27568                 contenteditable : 'true',
27569                 cn : [
27570                     img
27571                 ]
27572             };
27573         }
27574         
27575         
27576         if (this.video_url.length > 0) {
27577             img = {
27578                 tag : 'div',
27579                 cls : this.cls,
27580                 frameborder : 0,
27581                 allowfullscreen : true,
27582                 width : 420,  // these are for video tricks - that we replace the outer
27583                 height : 315,
27584                 src : this.video_url,
27585                 cn : [
27586                     img
27587                 ]
27588             };
27589         }
27590         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27591         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27592         
27593   
27594         var ret =   {
27595             tag: 'figure',
27596             'data-block' : 'Figure',
27597             'data-width' : this.width, 
27598             contenteditable : 'false',
27599             
27600             style : {
27601                 display: 'block',
27602                 float :  this.align ,
27603                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27604                 width : this.align == 'center' ? '100%' : this.width,
27605                 margin:  '0px',
27606                 padding: this.align == 'center' ? '0' : '0 10px' ,
27607                 textAlign : this.align   // seems to work for email..
27608                 
27609             },
27610            
27611             
27612             align : this.align,
27613             cn : [
27614                 img,
27615               
27616                 {
27617                     tag: 'figcaption',
27618                     'data-display' : this.caption_display,
27619                     style : {
27620                         textAlign : 'left',
27621                         fontSize : '16px',
27622                         lineHeight : '24px',
27623                         display : this.caption_display,
27624                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27625                         margin: m,
27626                         width: this.align == 'center' ?  this.width : '100%' 
27627                     
27628                          
27629                     },
27630                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27631                     cn : [
27632                         {
27633                             tag: 'div',
27634                             style  : {
27635                                 marginTop : '16px',
27636                                 textAlign : 'left'
27637                             },
27638                             align: 'left',
27639                             cn : [
27640                                 {
27641                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27642                                     tag : 'i',
27643                                     contenteditable : true,
27644                                     html : captionhtml
27645                                 }
27646                                 
27647                             ]
27648                         }
27649                         
27650                     ]
27651                     
27652                 }
27653             ]
27654         };
27655         return ret;
27656          
27657     },
27658     
27659     readElement : function(node)
27660     {
27661         // this should not really come from the link...
27662         this.video_url = this.getVal(node, 'div', 'src');
27663         this.cls = this.getVal(node, 'div', 'class');
27664         this.href = this.getVal(node, 'a', 'href');
27665         
27666         
27667         this.image_src = this.getVal(node, 'img', 'src');
27668          
27669         this.align = this.getVal(node, 'figure', 'align');
27670         var figcaption = this.getVal(node, 'figcaption', false);
27671         if (figcaption !== '') {
27672             this.caption = this.getVal(figcaption, 'i', 'html');
27673         }
27674         
27675
27676         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27677         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27678         this.width = this.getVal(node, true, 'data-width');
27679         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27680         
27681     },
27682     removeNode : function()
27683     {
27684         return this.node;
27685     }
27686     
27687   
27688    
27689      
27690     
27691     
27692     
27693     
27694 })
27695
27696  
27697
27698 /**
27699  * @class Roo.htmleditor.BlockTable
27700  * Block that manages a table
27701  * 
27702  * @constructor
27703  * Create a new Filter.
27704  * @param {Object} config Configuration options
27705  */
27706
27707 Roo.htmleditor.BlockTable = function(cfg)
27708 {
27709     if (cfg.node) {
27710         this.readElement(cfg.node);
27711         this.updateElement(cfg.node);
27712     }
27713     Roo.apply(this, cfg);
27714     if (!cfg.node) {
27715         this.rows = [];
27716         for(var r = 0; r < this.no_row; r++) {
27717             this.rows[r] = [];
27718             for(var c = 0; c < this.no_col; c++) {
27719                 this.rows[r][c] = this.emptyCell();
27720             }
27721         }
27722     }
27723     
27724     
27725 }
27726 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27727  
27728     rows : false,
27729     no_col : 1,
27730     no_row : 1,
27731     
27732     
27733     width: '100%',
27734     
27735     // used by context menu
27736     friendly_name : 'Table',
27737     deleteTitle : 'Delete Table',
27738     // context menu is drawn once..
27739     
27740     contextMenu : function(toolbar)
27741     {
27742         
27743         var block = function() {
27744             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27745         };
27746         
27747         
27748         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27749         
27750         var syncValue = toolbar.editorcore.syncValue;
27751         
27752         var fields = {};
27753         
27754         return [
27755             {
27756                 xtype : 'TextItem',
27757                 text : "Width: ",
27758                 xns : rooui.Toolbar  //Boostrap?
27759             },
27760             {
27761                 xtype : 'ComboBox',
27762                 allowBlank : false,
27763                 displayField : 'val',
27764                 editable : true,
27765                 listWidth : 100,
27766                 triggerAction : 'all',
27767                 typeAhead : true,
27768                 valueField : 'val',
27769                 width : 100,
27770                 name : 'width',
27771                 listeners : {
27772                     select : function (combo, r, index)
27773                     {
27774                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27775                         var b = block();
27776                         b.width = r.get('val');
27777                         b.updateElement();
27778                         syncValue();
27779                         toolbar.editorcore.onEditorEvent();
27780                     }
27781                 },
27782                 xns : rooui.form,
27783                 store : {
27784                     xtype : 'SimpleStore',
27785                     data : [
27786                         ['100%'],
27787                         ['auto']
27788                     ],
27789                     fields : [ 'val'],
27790                     xns : Roo.data
27791                 }
27792             },
27793             // -------- Cols
27794             
27795             {
27796                 xtype : 'TextItem',
27797                 text : "Columns: ",
27798                 xns : rooui.Toolbar  //Boostrap?
27799             },
27800          
27801             {
27802                 xtype : 'Button',
27803                 text: '-',
27804                 listeners : {
27805                     click : function (_self, e)
27806                     {
27807                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27808                         block().removeColumn();
27809                         syncValue();
27810                         toolbar.editorcore.onEditorEvent();
27811                     }
27812                 },
27813                 xns : rooui.Toolbar
27814             },
27815             {
27816                 xtype : 'Button',
27817                 text: '+',
27818                 listeners : {
27819                     click : function (_self, e)
27820                     {
27821                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27822                         block().addColumn();
27823                         syncValue();
27824                         toolbar.editorcore.onEditorEvent();
27825                     }
27826                 },
27827                 xns : rooui.Toolbar
27828             },
27829             // -------- ROWS
27830             {
27831                 xtype : 'TextItem',
27832                 text : "Rows: ",
27833                 xns : rooui.Toolbar  //Boostrap?
27834             },
27835          
27836             {
27837                 xtype : 'Button',
27838                 text: '-',
27839                 listeners : {
27840                     click : function (_self, e)
27841                     {
27842                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27843                         block().removeRow();
27844                         syncValue();
27845                         toolbar.editorcore.onEditorEvent();
27846                     }
27847                 },
27848                 xns : rooui.Toolbar
27849             },
27850             {
27851                 xtype : 'Button',
27852                 text: '+',
27853                 listeners : {
27854                     click : function (_self, e)
27855                     {
27856                         block().addRow();
27857                         syncValue();
27858                         toolbar.editorcore.onEditorEvent();
27859                     }
27860                 },
27861                 xns : rooui.Toolbar
27862             },
27863             // -------- ROWS
27864             {
27865                 xtype : 'Button',
27866                 text: 'Reset Column Widths',
27867                 listeners : {
27868                     
27869                     click : function (_self, e)
27870                     {
27871                         block().resetWidths();
27872                         syncValue();
27873                         toolbar.editorcore.onEditorEvent();
27874                     }
27875                 },
27876                 xns : rooui.Toolbar
27877             } 
27878             
27879             
27880             
27881         ];
27882         
27883     },
27884     
27885     
27886   /**
27887      * create a DomHelper friendly object - for use with
27888      * Roo.DomHelper.markup / overwrite / etc..
27889      * ?? should it be called with option to hide all editing features?
27890      */
27891     toObject : function()
27892     {
27893         
27894         var ret = {
27895             tag : 'table',
27896             contenteditable : 'false', // this stops cell selection from picking the table.
27897             'data-block' : 'Table',
27898             style : {
27899                 width:  this.width,
27900                 border : 'solid 1px #000', // ??? hard coded?
27901                 'border-collapse' : 'collapse' 
27902             },
27903             cn : [
27904                 { tag : 'tbody' , cn : [] }
27905             ]
27906         };
27907         
27908         // do we have a head = not really 
27909         var ncols = 0;
27910         Roo.each(this.rows, function( row ) {
27911             var tr = {
27912                 tag: 'tr',
27913                 style : {
27914                     margin: '6px',
27915                     border : 'solid 1px #000',
27916                     textAlign : 'left' 
27917                 },
27918                 cn : [ ]
27919             };
27920             
27921             ret.cn[0].cn.push(tr);
27922             // does the row have any properties? ?? height?
27923             var nc = 0;
27924             Roo.each(row, function( cell ) {
27925                 
27926                 var td = {
27927                     tag : 'td',
27928                     contenteditable :  'true',
27929                     'data-block' : 'Td',
27930                     html : cell.html,
27931                     style : cell.style
27932                 };
27933                 if (cell.colspan > 1) {
27934                     td.colspan = cell.colspan ;
27935                     nc += cell.colspan;
27936                 } else {
27937                     nc++;
27938                 }
27939                 if (cell.rowspan > 1) {
27940                     td.rowspan = cell.rowspan ;
27941                 }
27942                 
27943                 
27944                 // widths ?
27945                 tr.cn.push(td);
27946                     
27947                 
27948             }, this);
27949             ncols = Math.max(nc, ncols);
27950             
27951             
27952         }, this);
27953         // add the header row..
27954         
27955         ncols++;
27956          
27957         
27958         return ret;
27959          
27960     },
27961     
27962     readElement : function(node)
27963     {
27964         node  = node ? node : this.node ;
27965         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27966         
27967         this.rows = [];
27968         this.no_row = 0;
27969         var trs = Array.from(node.rows);
27970         trs.forEach(function(tr) {
27971             var row =  [];
27972             this.rows.push(row);
27973             
27974             this.no_row++;
27975             var no_column = 0;
27976             Array.from(tr.cells).forEach(function(td) {
27977                 
27978                 var add = {
27979                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27980                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27981                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27982                     html : td.innerHTML
27983                 };
27984                 no_column += add.colspan;
27985                      
27986                 
27987                 row.push(add);
27988                 
27989                 
27990             },this);
27991             this.no_col = Math.max(this.no_col, no_column);
27992             
27993             
27994         },this);
27995         
27996         
27997     },
27998     normalizeRows: function()
27999     {
28000         var ret= [];
28001         var rid = -1;
28002         this.rows.forEach(function(row) {
28003             rid++;
28004             ret[rid] = [];
28005             row = this.normalizeRow(row);
28006             var cid = 0;
28007             row.forEach(function(c) {
28008                 while (typeof(ret[rid][cid]) != 'undefined') {
28009                     cid++;
28010                 }
28011                 if (typeof(ret[rid]) == 'undefined') {
28012                     ret[rid] = [];
28013                 }
28014                 ret[rid][cid] = c;
28015                 c.row = rid;
28016                 c.col = cid;
28017                 if (c.rowspan < 2) {
28018                     return;
28019                 }
28020                 
28021                 for(var i = 1 ;i < c.rowspan; i++) {
28022                     if (typeof(ret[rid+i]) == 'undefined') {
28023                         ret[rid+i] = [];
28024                     }
28025                     ret[rid+i][cid] = c;
28026                 }
28027             });
28028         }, this);
28029         return ret;
28030     
28031     },
28032     
28033     normalizeRow: function(row)
28034     {
28035         var ret= [];
28036         row.forEach(function(c) {
28037             if (c.colspan < 2) {
28038                 ret.push(c);
28039                 return;
28040             }
28041             for(var i =0 ;i < c.colspan; i++) {
28042                 ret.push(c);
28043             }
28044         });
28045         return ret;
28046     
28047     },
28048     
28049     deleteColumn : function(sel)
28050     {
28051         if (!sel || sel.type != 'col') {
28052             return;
28053         }
28054         if (this.no_col < 2) {
28055             return;
28056         }
28057         
28058         this.rows.forEach(function(row) {
28059             var cols = this.normalizeRow(row);
28060             var col = cols[sel.col];
28061             if (col.colspan > 1) {
28062                 col.colspan --;
28063             } else {
28064                 row.remove(col);
28065             }
28066             
28067         }, this);
28068         this.no_col--;
28069         
28070     },
28071     removeColumn : function()
28072     {
28073         this.deleteColumn({
28074             type: 'col',
28075             col : this.no_col-1
28076         });
28077         this.updateElement();
28078     },
28079     
28080      
28081     addColumn : function()
28082     {
28083         
28084         this.rows.forEach(function(row) {
28085             row.push(this.emptyCell());
28086            
28087         }, this);
28088         this.updateElement();
28089     },
28090     
28091     deleteRow : function(sel)
28092     {
28093         if (!sel || sel.type != 'row') {
28094             return;
28095         }
28096         
28097         if (this.no_row < 2) {
28098             return;
28099         }
28100         
28101         var rows = this.normalizeRows();
28102         
28103         
28104         rows[sel.row].forEach(function(col) {
28105             if (col.rowspan > 1) {
28106                 col.rowspan--;
28107             } else {
28108                 col.remove = 1; // flage it as removed.
28109             }
28110             
28111         }, this);
28112         var newrows = [];
28113         this.rows.forEach(function(row) {
28114             newrow = [];
28115             row.forEach(function(c) {
28116                 if (typeof(c.remove) == 'undefined') {
28117                     newrow.push(c);
28118                 }
28119                 
28120             });
28121             if (newrow.length > 0) {
28122                 newrows.push(row);
28123             }
28124         });
28125         this.rows =  newrows;
28126         
28127         
28128         
28129         this.no_row--;
28130         this.updateElement();
28131         
28132     },
28133     removeRow : function()
28134     {
28135         this.deleteRow({
28136             type: 'row',
28137             row : this.no_row-1
28138         });
28139         
28140     },
28141     
28142      
28143     addRow : function()
28144     {
28145         
28146         var row = [];
28147         for (var i = 0; i < this.no_col; i++ ) {
28148             
28149             row.push(this.emptyCell());
28150            
28151         }
28152         this.rows.push(row);
28153         this.updateElement();
28154         
28155     },
28156      
28157     // the default cell object... at present...
28158     emptyCell : function() {
28159         return (new Roo.htmleditor.BlockTd({})).toObject();
28160         
28161      
28162     },
28163     
28164     removeNode : function()
28165     {
28166         return this.node;
28167     },
28168     
28169     
28170     
28171     resetWidths : function()
28172     {
28173         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28174             var nn = Roo.htmleditor.Block.factory(n);
28175             nn.width = '';
28176             nn.updateElement(n);
28177         });
28178     }
28179     
28180     
28181     
28182     
28183 })
28184
28185 /**
28186  *
28187  * editing a TD?
28188  *
28189  * since selections really work on the table cell, then editing really should work from there
28190  *
28191  * The original plan was to support merging etc... - but that may not be needed yet..
28192  *
28193  * So this simple version will support:
28194  *   add/remove cols
28195  *   adjust the width +/-
28196  *   reset the width...
28197  *   
28198  *
28199  */
28200
28201
28202  
28203
28204 /**
28205  * @class Roo.htmleditor.BlockTable
28206  * Block that manages a table
28207  * 
28208  * @constructor
28209  * Create a new Filter.
28210  * @param {Object} config Configuration options
28211  */
28212
28213 Roo.htmleditor.BlockTd = function(cfg)
28214 {
28215     if (cfg.node) {
28216         this.readElement(cfg.node);
28217         this.updateElement(cfg.node);
28218     }
28219     Roo.apply(this, cfg);
28220      
28221     
28222     
28223 }
28224 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28225  
28226     node : false,
28227     
28228     width: '',
28229     textAlign : 'left',
28230     valign : 'top',
28231     
28232     colspan : 1,
28233     rowspan : 1,
28234     
28235     
28236     // used by context menu
28237     friendly_name : 'Table Cell',
28238     deleteTitle : false, // use our customer delete
28239     
28240     // context menu is drawn once..
28241     
28242     contextMenu : function(toolbar)
28243     {
28244         
28245         var cell = function() {
28246             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28247         };
28248         
28249         var table = function() {
28250             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28251         };
28252         
28253         var lr = false;
28254         var saveSel = function()
28255         {
28256             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28257         }
28258         var restoreSel = function()
28259         {
28260             if (lr) {
28261                 (function() {
28262                     toolbar.editorcore.focus();
28263                     var cr = toolbar.editorcore.getSelection();
28264                     cr.removeAllRanges();
28265                     cr.addRange(lr);
28266                     toolbar.editorcore.onEditorEvent();
28267                 }).defer(10, this);
28268                 
28269                 
28270             }
28271         }
28272         
28273         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28274         
28275         var syncValue = toolbar.editorcore.syncValue;
28276         
28277         var fields = {};
28278         
28279         return [
28280             {
28281                 xtype : 'Button',
28282                 text : 'Edit Table',
28283                 listeners : {
28284                     click : function() {
28285                         var t = toolbar.tb.selectedNode.closest('table');
28286                         toolbar.editorcore.selectNode(t);
28287                         toolbar.editorcore.onEditorEvent();                        
28288                     }
28289                 }
28290                 
28291             },
28292               
28293            
28294              
28295             {
28296                 xtype : 'TextItem',
28297                 text : "Column Width: ",
28298                  xns : rooui.Toolbar 
28299                
28300             },
28301             {
28302                 xtype : 'Button',
28303                 text: '-',
28304                 listeners : {
28305                     click : function (_self, e)
28306                     {
28307                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28308                         cell().shrinkColumn();
28309                         syncValue();
28310                          toolbar.editorcore.onEditorEvent();
28311                     }
28312                 },
28313                 xns : rooui.Toolbar
28314             },
28315             {
28316                 xtype : 'Button',
28317                 text: '+',
28318                 listeners : {
28319                     click : function (_self, e)
28320                     {
28321                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28322                         cell().growColumn();
28323                         syncValue();
28324                         toolbar.editorcore.onEditorEvent();
28325                     }
28326                 },
28327                 xns : rooui.Toolbar
28328             },
28329             
28330             {
28331                 xtype : 'TextItem',
28332                 text : "Vertical Align: ",
28333                 xns : rooui.Toolbar  //Boostrap?
28334             },
28335             {
28336                 xtype : 'ComboBox',
28337                 allowBlank : false,
28338                 displayField : 'val',
28339                 editable : true,
28340                 listWidth : 100,
28341                 triggerAction : 'all',
28342                 typeAhead : true,
28343                 valueField : 'val',
28344                 width : 100,
28345                 name : 'valign',
28346                 listeners : {
28347                     select : function (combo, r, index)
28348                     {
28349                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28350                         var b = cell();
28351                         b.valign = r.get('val');
28352                         b.updateElement();
28353                         syncValue();
28354                         toolbar.editorcore.onEditorEvent();
28355                     }
28356                 },
28357                 xns : rooui.form,
28358                 store : {
28359                     xtype : 'SimpleStore',
28360                     data : [
28361                         ['top'],
28362                         ['middle'],
28363                         ['bottom'] // there are afew more... 
28364                     ],
28365                     fields : [ 'val'],
28366                     xns : Roo.data
28367                 }
28368             },
28369             
28370             {
28371                 xtype : 'TextItem',
28372                 text : "Merge Cells: ",
28373                  xns : rooui.Toolbar 
28374                
28375             },
28376             
28377             
28378             {
28379                 xtype : 'Button',
28380                 text: 'Right',
28381                 listeners : {
28382                     click : function (_self, e)
28383                     {
28384                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28385                         cell().mergeRight();
28386                         //block().growColumn();
28387                         syncValue();
28388                         toolbar.editorcore.onEditorEvent();
28389                     }
28390                 },
28391                 xns : rooui.Toolbar
28392             },
28393              
28394             {
28395                 xtype : 'Button',
28396                 text: 'Below',
28397                 listeners : {
28398                     click : function (_self, e)
28399                     {
28400                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28401                         cell().mergeBelow();
28402                         //block().growColumn();
28403                         syncValue();
28404                         toolbar.editorcore.onEditorEvent();
28405                     }
28406                 },
28407                 xns : rooui.Toolbar
28408             },
28409             {
28410                 xtype : 'TextItem',
28411                 text : "| ",
28412                  xns : rooui.Toolbar 
28413                
28414             },
28415             
28416             {
28417                 xtype : 'Button',
28418                 text: 'Split',
28419                 listeners : {
28420                     click : function (_self, e)
28421                     {
28422                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28423                         cell().split();
28424                         syncValue();
28425                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28426                         toolbar.editorcore.onEditorEvent();
28427                                              
28428                     }
28429                 },
28430                 xns : rooui.Toolbar
28431             },
28432             {
28433                 xtype : 'Fill',
28434                 xns : rooui.Toolbar 
28435                
28436             },
28437         
28438           
28439             {
28440                 xtype : 'Button',
28441                 text: 'Delete',
28442                  
28443                 xns : rooui.Toolbar,
28444                 menu : {
28445                     xtype : 'Menu',
28446                     xns : rooui.menu,
28447                     items : [
28448                         {
28449                             xtype : 'Item',
28450                             html: 'Column',
28451                             listeners : {
28452                                 click : function (_self, e)
28453                                 {
28454                                     var t = table();
28455                                     
28456                                     cell().deleteColumn();
28457                                     syncValue();
28458                                     toolbar.editorcore.selectNode(t.node);
28459                                     toolbar.editorcore.onEditorEvent();   
28460                                 }
28461                             },
28462                             xns : rooui.menu
28463                         },
28464                         {
28465                             xtype : 'Item',
28466                             html: 'Row',
28467                             listeners : {
28468                                 click : function (_self, e)
28469                                 {
28470                                     var t = table();
28471                                     cell().deleteRow();
28472                                     syncValue();
28473                                     
28474                                     toolbar.editorcore.selectNode(t.node);
28475                                     toolbar.editorcore.onEditorEvent();   
28476                                                          
28477                                 }
28478                             },
28479                             xns : rooui.menu
28480                         },
28481                        {
28482                             xtype : 'Separator',
28483                             xns : rooui.menu
28484                         },
28485                         {
28486                             xtype : 'Item',
28487                             html: 'Table',
28488                             listeners : {
28489                                 click : function (_self, e)
28490                                 {
28491                                     var t = table();
28492                                     var nn = t.node.nextSibling || t.node.previousSibling;
28493                                     t.node.parentNode.removeChild(t.node);
28494                                     if (nn) { 
28495                                         toolbar.editorcore.selectNode(nn, true);
28496                                     }
28497                                     toolbar.editorcore.onEditorEvent();   
28498                                                          
28499                                 }
28500                             },
28501                             xns : rooui.menu
28502                         }
28503                     ]
28504                 }
28505             }
28506             
28507             // align... << fixme
28508             
28509         ];
28510         
28511     },
28512     
28513     
28514   /**
28515      * create a DomHelper friendly object - for use with
28516      * Roo.DomHelper.markup / overwrite / etc..
28517      * ?? should it be called with option to hide all editing features?
28518      */
28519  /**
28520      * create a DomHelper friendly object - for use with
28521      * Roo.DomHelper.markup / overwrite / etc..
28522      * ?? should it be called with option to hide all editing features?
28523      */
28524     toObject : function()
28525     {
28526         var ret = {
28527             tag : 'td',
28528             contenteditable : 'true', // this stops cell selection from picking the table.
28529             'data-block' : 'Td',
28530             valign : this.valign,
28531             style : {  
28532                 'text-align' :  this.textAlign,
28533                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28534                 'border-collapse' : 'collapse',
28535                 padding : '6px', // 8 for desktop / 4 for mobile
28536                 'vertical-align': this.valign
28537             },
28538             html : this.html
28539         };
28540         if (this.width != '') {
28541             ret.width = this.width;
28542             ret.style.width = this.width;
28543         }
28544         
28545         
28546         if (this.colspan > 1) {
28547             ret.colspan = this.colspan ;
28548         } 
28549         if (this.rowspan > 1) {
28550             ret.rowspan = this.rowspan ;
28551         }
28552         
28553            
28554         
28555         return ret;
28556          
28557     },
28558     
28559     readElement : function(node)
28560     {
28561         node  = node ? node : this.node ;
28562         this.width = node.style.width;
28563         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28564         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28565         this.html = node.innerHTML;
28566         if (node.style.textAlign != '') {
28567             this.textAlign = node.style.textAlign;
28568         }
28569         
28570         
28571     },
28572      
28573     // the default cell object... at present...
28574     emptyCell : function() {
28575         return {
28576             colspan :  1,
28577             rowspan :  1,
28578             textAlign : 'left',
28579             html : "&nbsp;" // is this going to be editable now?
28580         };
28581      
28582     },
28583     
28584     removeNode : function()
28585     {
28586         return this.node.closest('table');
28587          
28588     },
28589     
28590     cellData : false,
28591     
28592     colWidths : false,
28593     
28594     toTableArray  : function()
28595     {
28596         var ret = [];
28597         var tab = this.node.closest('tr').closest('table');
28598         Array.from(tab.rows).forEach(function(r, ri){
28599             ret[ri] = [];
28600         });
28601         var rn = 0;
28602         this.colWidths = [];
28603         var all_auto = true;
28604         Array.from(tab.rows).forEach(function(r, ri){
28605             
28606             var cn = 0;
28607             Array.from(r.cells).forEach(function(ce, ci){
28608                 var c =  {
28609                     cell : ce,
28610                     row : rn,
28611                     col: cn,
28612                     colspan : ce.colSpan,
28613                     rowspan : ce.rowSpan
28614                 };
28615                 if (ce.isEqualNode(this.node)) {
28616                     this.cellData = c;
28617                 }
28618                 // if we have been filled up by a row?
28619                 if (typeof(ret[rn][cn]) != 'undefined') {
28620                     while(typeof(ret[rn][cn]) != 'undefined') {
28621                         cn++;
28622                     }
28623                     c.col = cn;
28624                 }
28625                 
28626                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
28627                     this.colWidths[cn] =   ce.style.width;
28628                     if (this.colWidths[cn] != '') {
28629                         all_auto = false;
28630                     }
28631                 }
28632                 
28633                 
28634                 if (c.colspan < 2 && c.rowspan < 2 ) {
28635                     ret[rn][cn] = c;
28636                     cn++;
28637                     return;
28638                 }
28639                 for(var j = 0; j < c.rowspan; j++) {
28640                     if (typeof(ret[rn+j]) == 'undefined') {
28641                         continue; // we have a problem..
28642                     }
28643                     ret[rn+j][cn] = c;
28644                     for(var i = 0; i < c.colspan; i++) {
28645                         ret[rn+j][cn+i] = c;
28646                     }
28647                 }
28648                 
28649                 cn += c.colspan;
28650             }, this);
28651             rn++;
28652         }, this);
28653         
28654         // initalize widths.?
28655         // either all widths or no widths..
28656         if (all_auto) {
28657             this.colWidths[0] = false; // no widths flag.
28658         }
28659         
28660         
28661         return ret;
28662         
28663     },
28664     
28665     
28666     
28667     
28668     mergeRight: function()
28669     {
28670          
28671         // get the contents of the next cell along..
28672         var tr = this.node.closest('tr');
28673         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28674         if (i >= tr.childNodes.length - 1) {
28675             return; // no cells on right to merge with.
28676         }
28677         var table = this.toTableArray();
28678         
28679         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28680             return; // nothing right?
28681         }
28682         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28683         // right cell - must be same rowspan and on the same row.
28684         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28685             return; // right hand side is not same rowspan.
28686         }
28687         
28688         
28689         
28690         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28691         tr.removeChild(rc.cell);
28692         this.colspan += rc.colspan;
28693         this.node.setAttribute('colspan', this.colspan);
28694
28695         var table = this.toTableArray();
28696         this.normalizeWidths(table);
28697         this.updateWidths(table);
28698     },
28699     
28700     
28701     mergeBelow : function()
28702     {
28703         var table = this.toTableArray();
28704         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28705             return; // no row below
28706         }
28707         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28708             return; // nothing right?
28709         }
28710         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28711         
28712         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28713             return; // right hand side is not same rowspan.
28714         }
28715         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28716         rc.cell.parentNode.removeChild(rc.cell);
28717         this.rowspan += rc.rowspan;
28718         this.node.setAttribute('rowspan', this.rowspan);
28719     },
28720     
28721     split: function()
28722     {
28723         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28724             return;
28725         }
28726         var table = this.toTableArray();
28727         var cd = this.cellData;
28728         this.rowspan = 1;
28729         this.colspan = 1;
28730         
28731         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28732              
28733             
28734             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28735                 if (r == cd.row && c == cd.col) {
28736                     this.node.removeAttribute('rowspan');
28737                     this.node.removeAttribute('colspan');
28738                 }
28739                  
28740                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28741                 ntd.removeAttribute('id'); 
28742                 ntd.style.width  = this.colWidths[c];
28743                 ntd.innerHTML = '';
28744                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28745             }
28746             
28747         }
28748         this.redrawAllCells(table);
28749         
28750     },
28751     
28752     
28753     
28754     redrawAllCells: function(table)
28755     {
28756         
28757          
28758         var tab = this.node.closest('tr').closest('table');
28759         var ctr = tab.rows[0].parentNode;
28760         Array.from(tab.rows).forEach(function(r, ri){
28761             
28762             Array.from(r.cells).forEach(function(ce, ci){
28763                 ce.parentNode.removeChild(ce);
28764             });
28765             r.parentNode.removeChild(r);
28766         });
28767         for(var r = 0 ; r < table.length; r++) {
28768             var re = tab.rows[r];
28769             
28770             var re = tab.ownerDocument.createElement('tr');
28771             ctr.appendChild(re);
28772             for(var c = 0 ; c < table[r].length; c++) {
28773                 if (table[r][c].cell === false) {
28774                     continue;
28775                 }
28776                 
28777                 re.appendChild(table[r][c].cell);
28778                  
28779                 table[r][c].cell = false;
28780             }
28781         }
28782         
28783     },
28784     updateWidths : function(table)
28785     {
28786         for(var r = 0 ; r < table.length; r++) {
28787            
28788             for(var c = 0 ; c < table[r].length; c++) {
28789                 if (table[r][c].cell === false) {
28790                     continue;
28791                 }
28792                 
28793                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28794                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28795                     el.width = Math.floor(this.colWidths[c])  +'%';
28796                     el.updateElement(el.node);
28797                 }
28798                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
28799                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28800                     var width = 0;
28801                     for(var i = 0; i < table[r][c].colspan; i ++) {
28802                         width += Math.floor(this.colWidths[c + i]);
28803                     }
28804                     el.width = width  +'%';
28805                     el.updateElement(el.node);
28806                 }
28807                 table[r][c].cell = false; // done
28808             }
28809         }
28810     },
28811     normalizeWidths : function(table)
28812     {
28813         if (this.colWidths[0] === false) {
28814             var nw = 100.0 / this.colWidths.length;
28815             this.colWidths.forEach(function(w,i) {
28816                 this.colWidths[i] = nw;
28817             },this);
28818             return;
28819         }
28820     
28821         var t = 0, missing = [];
28822         
28823         this.colWidths.forEach(function(w,i) {
28824             //if you mix % and
28825             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28826             var add =  this.colWidths[i];
28827             if (add > 0) {
28828                 t+=add;
28829                 return;
28830             }
28831             missing.push(i);
28832             
28833             
28834         },this);
28835         var nc = this.colWidths.length;
28836         if (missing.length) {
28837             var mult = (nc - missing.length) / (1.0 * nc);
28838             var t = mult * t;
28839             var ew = (100 -t) / (1.0 * missing.length);
28840             this.colWidths.forEach(function(w,i) {
28841                 if (w > 0) {
28842                     this.colWidths[i] = w * mult;
28843                     return;
28844                 }
28845                 
28846                 this.colWidths[i] = ew;
28847             }, this);
28848             // have to make up numbers..
28849              
28850         }
28851         // now we should have all the widths..
28852         
28853     
28854     },
28855     
28856     shrinkColumn : function()
28857     {
28858         var table = this.toTableArray();
28859         this.normalizeWidths(table);
28860         var col = this.cellData.col;
28861         var nw = this.colWidths[col] * 0.8;
28862         if (nw < 5) {
28863             return;
28864         }
28865         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28866         this.colWidths.forEach(function(w,i) {
28867             if (i == col) {
28868                  this.colWidths[i] = nw;
28869                 return;
28870             }
28871             this.colWidths[i] += otherAdd
28872         }, this);
28873         this.updateWidths(table);
28874          
28875     },
28876     growColumn : function()
28877     {
28878         var table = this.toTableArray();
28879         this.normalizeWidths(table);
28880         var col = this.cellData.col;
28881         var nw = this.colWidths[col] * 1.2;
28882         if (nw > 90) {
28883             return;
28884         }
28885         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28886         this.colWidths.forEach(function(w,i) {
28887             if (i == col) {
28888                 this.colWidths[i] = nw;
28889                 return;
28890             }
28891             this.colWidths[i] -= otherSub
28892         }, this);
28893         this.updateWidths(table);
28894          
28895     },
28896     deleteRow : function()
28897     {
28898         // delete this rows 'tr'
28899         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28900         // then reduce the rowspan.
28901         var table = this.toTableArray();
28902         // this.cellData.row;
28903         for (var i =0;i< table[this.cellData.row].length ; i++) {
28904             var c = table[this.cellData.row][i];
28905             if (c.row != this.cellData.row) {
28906                 
28907                 c.rowspan--;
28908                 c.cell.setAttribute('rowspan', c.rowspan);
28909                 continue;
28910             }
28911             if (c.rowspan > 1) {
28912                 c.rowspan--;
28913                 c.cell.setAttribute('rowspan', c.rowspan);
28914             }
28915         }
28916         table.splice(this.cellData.row,1);
28917         this.redrawAllCells(table);
28918         
28919     },
28920     deleteColumn : function()
28921     {
28922         var table = this.toTableArray();
28923         
28924         for (var i =0;i< table.length ; i++) {
28925             var c = table[i][this.cellData.col];
28926             if (c.col != this.cellData.col) {
28927                 table[i][this.cellData.col].colspan--;
28928             } else if (c.colspan > 1) {
28929                 c.colspan--;
28930                 c.cell.setAttribute('colspan', c.colspan);
28931             }
28932             table[i].splice(this.cellData.col,1);
28933         }
28934         
28935         this.redrawAllCells(table);
28936     }
28937     
28938     
28939     
28940     
28941 })
28942
28943 //<script type="text/javascript">
28944
28945 /*
28946  * Based  Ext JS Library 1.1.1
28947  * Copyright(c) 2006-2007, Ext JS, LLC.
28948  * LGPL
28949  *
28950  */
28951  
28952 /**
28953  * @class Roo.HtmlEditorCore
28954  * @extends Roo.Component
28955  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28956  *
28957  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28958  */
28959
28960 Roo.HtmlEditorCore = function(config){
28961     
28962     
28963     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28964     
28965     
28966     this.addEvents({
28967         /**
28968          * @event initialize
28969          * Fires when the editor is fully initialized (including the iframe)
28970          * @param {Roo.HtmlEditorCore} this
28971          */
28972         initialize: true,
28973         /**
28974          * @event activate
28975          * Fires when the editor is first receives the focus. Any insertion must wait
28976          * until after this event.
28977          * @param {Roo.HtmlEditorCore} this
28978          */
28979         activate: true,
28980          /**
28981          * @event beforesync
28982          * Fires before the textarea is updated with content from the editor iframe. Return false
28983          * to cancel the sync.
28984          * @param {Roo.HtmlEditorCore} this
28985          * @param {String} html
28986          */
28987         beforesync: true,
28988          /**
28989          * @event beforepush
28990          * Fires before the iframe editor is updated with content from the textarea. Return false
28991          * to cancel the push.
28992          * @param {Roo.HtmlEditorCore} this
28993          * @param {String} html
28994          */
28995         beforepush: true,
28996          /**
28997          * @event sync
28998          * Fires when the textarea is updated with content from the editor iframe.
28999          * @param {Roo.HtmlEditorCore} this
29000          * @param {String} html
29001          */
29002         sync: true,
29003          /**
29004          * @event push
29005          * Fires when the iframe editor is updated with content from the textarea.
29006          * @param {Roo.HtmlEditorCore} this
29007          * @param {String} html
29008          */
29009         push: true,
29010         
29011         /**
29012          * @event editorevent
29013          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
29014          * @param {Roo.HtmlEditorCore} this
29015          */
29016         editorevent: true 
29017          
29018         
29019     });
29020     
29021     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
29022     
29023     // defaults : white / black...
29024     this.applyBlacklists();
29025     
29026     
29027     
29028 };
29029
29030
29031 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
29032
29033
29034      /**
29035      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
29036      */
29037     
29038     owner : false,
29039     
29040      /**
29041      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
29042      *                        Roo.resizable.
29043      */
29044     resizable : false,
29045      /**
29046      * @cfg {Number} height (in pixels)
29047      */   
29048     height: 300,
29049    /**
29050      * @cfg {Number} width (in pixels)
29051      */   
29052     width: 500,
29053      /**
29054      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
29055      *         if you are doing an email editor, this probably needs disabling, it's designed
29056      */
29057     autoClean: true,
29058     
29059     /**
29060      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
29061      */
29062     enableBlocks : true,
29063     /**
29064      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
29065      * 
29066      */
29067     stylesheets: false,
29068      /**
29069      * @cfg {String} language default en - language of text (usefull for rtl languages)
29070      * 
29071      */
29072     language: 'en',
29073     
29074     /**
29075      * @cfg {boolean} allowComments - default false - allow comments in HTML source
29076      *          - by default they are stripped - if you are editing email you may need this.
29077      */
29078     allowComments: false,
29079     // id of frame..
29080     frameId: false,
29081     
29082     // private properties
29083     validationEvent : false,
29084     deferHeight: true,
29085     initialized : false,
29086     activated : false,
29087     sourceEditMode : false,
29088     onFocus : Roo.emptyFn,
29089     iframePad:3,
29090     hideMode:'offsets',
29091     
29092     clearUp: true,
29093     
29094     // blacklist + whitelisted elements..
29095     black: false,
29096     white: false,
29097      
29098     bodyCls : '',
29099
29100     
29101     undoManager : false,
29102     /**
29103      * Protected method that will not generally be called directly. It
29104      * is called when the editor initializes the iframe with HTML contents. Override this method if you
29105      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
29106      */
29107     getDocMarkup : function(){
29108         // body styles..
29109         var st = '';
29110         
29111         // inherit styels from page...?? 
29112         if (this.stylesheets === false) {
29113             
29114             Roo.get(document.head).select('style').each(function(node) {
29115                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29116             });
29117             
29118             Roo.get(document.head).select('link').each(function(node) { 
29119                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29120             });
29121             
29122         } else if (!this.stylesheets.length) {
29123                 // simple..
29124                 st = '<style type="text/css">' +
29125                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29126                    '</style>';
29127         } else {
29128             for (var i in this.stylesheets) {
29129                 if (typeof(this.stylesheets[i]) != 'string') {
29130                     continue;
29131                 }
29132                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
29133             }
29134             
29135         }
29136         
29137         st +=  '<style type="text/css">' +
29138             'IMG { cursor: pointer } ' +
29139         '</style>';
29140         
29141         st += '<meta name="google" content="notranslate">';
29142         
29143         var cls = 'notranslate roo-htmleditor-body';
29144         
29145         if(this.bodyCls.length){
29146             cls += ' ' + this.bodyCls;
29147         }
29148         
29149         return '<html  class="notranslate" translate="no"><head>' + st  +
29150             //<style type="text/css">' +
29151             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29152             //'</style>' +
29153             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
29154     },
29155
29156     // private
29157     onRender : function(ct, position)
29158     {
29159         var _t = this;
29160         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
29161         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
29162         
29163         
29164         this.el.dom.style.border = '0 none';
29165         this.el.dom.setAttribute('tabIndex', -1);
29166         this.el.addClass('x-hidden hide');
29167         
29168         
29169         
29170         if(Roo.isIE){ // fix IE 1px bogus margin
29171             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29172         }
29173        
29174         
29175         this.frameId = Roo.id();
29176         
29177          
29178         
29179         var iframe = this.owner.wrap.createChild({
29180             tag: 'iframe',
29181             cls: 'form-control', // bootstrap..
29182             id: this.frameId,
29183             name: this.frameId,
29184             frameBorder : 'no',
29185             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29186         }, this.el
29187         );
29188         
29189         
29190         this.iframe = iframe.dom;
29191
29192         this.assignDocWin();
29193         
29194         this.doc.designMode = 'on';
29195        
29196         this.doc.open();
29197         this.doc.write(this.getDocMarkup());
29198         this.doc.close();
29199
29200         
29201         var task = { // must defer to wait for browser to be ready
29202             run : function(){
29203                 //console.log("run task?" + this.doc.readyState);
29204                 this.assignDocWin();
29205                 if(this.doc.body || this.doc.readyState == 'complete'){
29206                     try {
29207                         this.doc.designMode="on";
29208                         
29209                     } catch (e) {
29210                         return;
29211                     }
29212                     Roo.TaskMgr.stop(task);
29213                     this.initEditor.defer(10, this);
29214                 }
29215             },
29216             interval : 10,
29217             duration: 10000,
29218             scope: this
29219         };
29220         Roo.TaskMgr.start(task);
29221
29222     },
29223
29224     // private
29225     onResize : function(w, h)
29226     {
29227          Roo.log('resize: ' +w + ',' + h );
29228         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29229         if(!this.iframe){
29230             return;
29231         }
29232         if(typeof w == 'number'){
29233             
29234             this.iframe.style.width = w + 'px';
29235         }
29236         if(typeof h == 'number'){
29237             
29238             this.iframe.style.height = h + 'px';
29239             if(this.doc){
29240                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29241             }
29242         }
29243         
29244     },
29245
29246     /**
29247      * Toggles the editor between standard and source edit mode.
29248      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29249      */
29250     toggleSourceEdit : function(sourceEditMode){
29251         
29252         this.sourceEditMode = sourceEditMode === true;
29253         
29254         if(this.sourceEditMode){
29255  
29256             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29257             
29258         }else{
29259             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29260             //this.iframe.className = '';
29261             this.deferFocus();
29262         }
29263         //this.setSize(this.owner.wrap.getSize());
29264         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29265     },
29266
29267     
29268   
29269
29270     /**
29271      * Protected method that will not generally be called directly. If you need/want
29272      * custom HTML cleanup, this is the method you should override.
29273      * @param {String} html The HTML to be cleaned
29274      * return {String} The cleaned HTML
29275      */
29276     cleanHtml : function(html)
29277     {
29278         html = String(html);
29279         if(html.length > 5){
29280             if(Roo.isSafari){ // strip safari nonsense
29281                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29282             }
29283         }
29284         if(html == '&nbsp;'){
29285             html = '';
29286         }
29287         return html;
29288     },
29289
29290     /**
29291      * HTML Editor -> Textarea
29292      * Protected method that will not generally be called directly. Syncs the contents
29293      * of the editor iframe with the textarea.
29294      */
29295     syncValue : function()
29296     {
29297         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29298         if(this.initialized){
29299             
29300             if (this.undoManager) {
29301                 this.undoManager.addEvent();
29302             }
29303
29304             
29305             var bd = (this.doc.body || this.doc.documentElement);
29306            
29307             
29308             var sel = this.win.getSelection();
29309             
29310             var div = document.createElement('div');
29311             div.innerHTML = bd.innerHTML;
29312             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29313             if (gtx.length > 0) {
29314                 var rm = gtx.item(0).parentNode;
29315                 rm.parentNode.removeChild(rm);
29316             }
29317             
29318            
29319             if (this.enableBlocks) {
29320                 new Roo.htmleditor.FilterBlock({ node : div });
29321             }
29322             
29323             var html = div.innerHTML;
29324             
29325             //?? tidy?
29326             if (this.autoClean) {
29327                 
29328                 new Roo.htmleditor.FilterAttributes({
29329                     node : div,
29330                     attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29331                     attrib_clean : ['href', 'src' ] 
29332                 });
29333                 
29334                 var tidy = new Roo.htmleditor.TidySerializer({
29335                     inner:  true
29336                 });
29337                 html  = tidy.serialize(div);
29338                 
29339             }
29340             
29341             
29342             if(Roo.isSafari){
29343                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29344                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29345                 if(m && m[1]){
29346                     html = '<div style="'+m[0]+'">' + html + '</div>';
29347                 }
29348             }
29349             html = this.cleanHtml(html);
29350             // fix up the special chars.. normaly like back quotes in word...
29351             // however we do not want to do this with chinese..
29352             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29353                 
29354                 var cc = match.charCodeAt();
29355
29356                 // Get the character value, handling surrogate pairs
29357                 if (match.length == 2) {
29358                     // It's a surrogate pair, calculate the Unicode code point
29359                     var high = match.charCodeAt(0) - 0xD800;
29360                     var low  = match.charCodeAt(1) - 0xDC00;
29361                     cc = (high * 0x400) + low + 0x10000;
29362                 }  else if (
29363                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29364                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29365                     (cc >= 0xf900 && cc < 0xfb00 )
29366                 ) {
29367                         return match;
29368                 }  
29369          
29370                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29371                 return "&#" + cc + ";";
29372                 
29373                 
29374             });
29375             
29376             
29377              
29378             if(this.owner.fireEvent('beforesync', this, html) !== false){
29379                 this.el.dom.value = html;
29380                 this.owner.fireEvent('sync', this, html);
29381             }
29382         }
29383     },
29384
29385     /**
29386      * TEXTAREA -> EDITABLE
29387      * Protected method that will not generally be called directly. Pushes the value of the textarea
29388      * into the iframe editor.
29389      */
29390     pushValue : function()
29391     {
29392         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29393         if(this.initialized){
29394             var v = this.el.dom.value.trim();
29395             
29396             
29397             if(this.owner.fireEvent('beforepush', this, v) !== false){
29398                 var d = (this.doc.body || this.doc.documentElement);
29399                 d.innerHTML = v;
29400                  
29401                 this.el.dom.value = d.innerHTML;
29402                 this.owner.fireEvent('push', this, v);
29403             }
29404             if (this.autoClean) {
29405                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29406                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29407             }
29408             if (this.enableBlocks) {
29409                 Roo.htmleditor.Block.initAll(this.doc.body);
29410             }
29411             
29412             this.updateLanguage();
29413             
29414             var lc = this.doc.body.lastChild;
29415             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29416                 // add an extra line at the end.
29417                 this.doc.body.appendChild(this.doc.createElement('br'));
29418             }
29419             
29420             
29421         }
29422     },
29423
29424     // private
29425     deferFocus : function(){
29426         this.focus.defer(10, this);
29427     },
29428
29429     // doc'ed in Field
29430     focus : function(){
29431         if(this.win && !this.sourceEditMode){
29432             this.win.focus();
29433         }else{
29434             this.el.focus();
29435         }
29436     },
29437     
29438     assignDocWin: function()
29439     {
29440         var iframe = this.iframe;
29441         
29442          if(Roo.isIE){
29443             this.doc = iframe.contentWindow.document;
29444             this.win = iframe.contentWindow;
29445         } else {
29446 //            if (!Roo.get(this.frameId)) {
29447 //                return;
29448 //            }
29449 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29450 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29451             
29452             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29453                 return;
29454             }
29455             
29456             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29457             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29458         }
29459     },
29460     
29461     // private
29462     initEditor : function(){
29463         //console.log("INIT EDITOR");
29464         this.assignDocWin();
29465         
29466         
29467         
29468         this.doc.designMode="on";
29469         this.doc.open();
29470         this.doc.write(this.getDocMarkup());
29471         this.doc.close();
29472         
29473         var dbody = (this.doc.body || this.doc.documentElement);
29474         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29475         // this copies styles from the containing element into thsi one..
29476         // not sure why we need all of this..
29477         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29478         
29479         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29480         //ss['background-attachment'] = 'fixed'; // w3c
29481         dbody.bgProperties = 'fixed'; // ie
29482         dbody.setAttribute("translate", "no");
29483         
29484         //Roo.DomHelper.applyStyles(dbody, ss);
29485         Roo.EventManager.on(this.doc, {
29486              
29487             'mouseup': this.onEditorEvent,
29488             'dblclick': this.onEditorEvent,
29489             'click': this.onEditorEvent,
29490             'keyup': this.onEditorEvent,
29491             
29492             buffer:100,
29493             scope: this
29494         });
29495         Roo.EventManager.on(this.doc, {
29496             'paste': this.onPasteEvent,
29497             scope : this
29498         });
29499         if(Roo.isGecko){
29500             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29501         }
29502         //??? needed???
29503         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29504             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29505         }
29506         this.initialized = true;
29507
29508         
29509         // initialize special key events - enter
29510         new Roo.htmleditor.KeyEnter({core : this});
29511         
29512          
29513         
29514         this.owner.fireEvent('initialize', this);
29515         this.pushValue();
29516     },
29517     // this is to prevent a href clicks resulting in a redirect?
29518    
29519     onPasteEvent : function(e,v)
29520     {
29521         // I think we better assume paste is going to be a dirty load of rubish from word..
29522         
29523         // even pasting into a 'email version' of this widget will have to clean up that mess.
29524         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29525         
29526         // check what type of paste - if it's an image, then handle it differently.
29527         if (cd.files && cd.files.length > 0) {
29528             // pasting images?
29529             var urlAPI = (window.createObjectURL && window) || 
29530                 (window.URL && URL.revokeObjectURL && URL) || 
29531                 (window.webkitURL && webkitURL);
29532     
29533             var url = urlAPI.createObjectURL( cd.files[0]);
29534             this.insertAtCursor('<img src=" + url + ">');
29535             return false;
29536         }
29537         if (cd.types.indexOf('text/html') < 0 ) {
29538             return false;
29539         }
29540         var images = [];
29541         var html = cd.getData('text/html'); // clipboard event
29542         if (cd.types.indexOf('text/rtf') > -1) {
29543             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29544             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29545         }
29546         //Roo.log(images);
29547         //Roo.log(imgs);
29548         // fixme..
29549         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29550                        .map(function(g) { return g.toDataURL(); })
29551                        .filter(function(g) { return g != 'about:blank'; });
29552         
29553         //Roo.log(html);
29554         html = this.cleanWordChars(html);
29555         
29556         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29557         
29558         
29559         var sn = this.getParentElement();
29560         // check if d contains a table, and prevent nesting??
29561         //Roo.log(d.getElementsByTagName('table'));
29562         //Roo.log(sn);
29563         //Roo.log(sn.closest('table'));
29564         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29565             e.preventDefault();
29566             this.insertAtCursor("You can not nest tables");
29567             //Roo.log("prevent?"); // fixme - 
29568             return false;
29569         }
29570         
29571         
29572         
29573         if (images.length > 0) {
29574             // replace all v:imagedata - with img.
29575             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
29576             Roo.each(ar, function(node) {
29577                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
29578                 node.parentNode.removeChild(node);
29579             });
29580             
29581             
29582             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29583                 img.setAttribute('src', images[i]);
29584             });
29585         }
29586         if (this.autoClean) {
29587             new Roo.htmleditor.FilterWord({ node : d });
29588             
29589             new Roo.htmleditor.FilterStyleToTag({ node : d });
29590             new Roo.htmleditor.FilterAttributes({
29591                 node : d,
29592                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29593                 attrib_clean : ['href', 'src' ] 
29594             });
29595             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29596             // should be fonts..
29597             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
29598             new Roo.htmleditor.FilterParagraph({ node : d });
29599             new Roo.htmleditor.FilterSpan({ node : d });
29600             new Roo.htmleditor.FilterLongBr({ node : d });
29601             new Roo.htmleditor.FilterComment({ node : d });
29602             
29603             
29604         }
29605         if (this.enableBlocks) {
29606                 
29607             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29608                 if (img.closest('figure')) { // assume!! that it's aready
29609                     return;
29610                 }
29611                 var fig  = new Roo.htmleditor.BlockFigure({
29612                     image_src  : img.src
29613                 });
29614                 fig.updateElement(img); // replace it..
29615                 
29616             });
29617         }
29618         
29619         
29620         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29621         if (this.enableBlocks) {
29622             Roo.htmleditor.Block.initAll(this.doc.body);
29623         }
29624          
29625         
29626         e.preventDefault();
29627         return false;
29628         // default behaveiour should be our local cleanup paste? (optional?)
29629         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29630         //this.owner.fireEvent('paste', e, v);
29631     },
29632     // private
29633     onDestroy : function(){
29634         
29635         
29636         
29637         if(this.rendered){
29638             
29639             //for (var i =0; i < this.toolbars.length;i++) {
29640             //    // fixme - ask toolbars for heights?
29641             //    this.toolbars[i].onDestroy();
29642            // }
29643             
29644             //this.wrap.dom.innerHTML = '';
29645             //this.wrap.remove();
29646         }
29647     },
29648
29649     // private
29650     onFirstFocus : function(){
29651         
29652         this.assignDocWin();
29653         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29654         
29655         this.activated = true;
29656          
29657     
29658         if(Roo.isGecko){ // prevent silly gecko errors
29659             this.win.focus();
29660             var s = this.win.getSelection();
29661             if(!s.focusNode || s.focusNode.nodeType != 3){
29662                 var r = s.getRangeAt(0);
29663                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29664                 r.collapse(true);
29665                 this.deferFocus();
29666             }
29667             try{
29668                 this.execCmd('useCSS', true);
29669                 this.execCmd('styleWithCSS', false);
29670             }catch(e){}
29671         }
29672         this.owner.fireEvent('activate', this);
29673     },
29674
29675     // private
29676     adjustFont: function(btn){
29677         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29678         //if(Roo.isSafari){ // safari
29679         //    adjust *= 2;
29680        // }
29681         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29682         if(Roo.isSafari){ // safari
29683             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29684             v =  (v < 10) ? 10 : v;
29685             v =  (v > 48) ? 48 : v;
29686             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29687             
29688         }
29689         
29690         
29691         v = Math.max(1, v+adjust);
29692         
29693         this.execCmd('FontSize', v  );
29694     },
29695
29696     onEditorEvent : function(e)
29697     {
29698          
29699         
29700         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29701             return; // we do not handle this.. (undo manager does..)
29702         }
29703         // in theory this detects if the last element is not a br, then we try and do that.
29704         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29705         if (e &&
29706             e.target.nodeName == 'BODY' &&
29707             e.type == "mouseup" &&
29708             this.doc.body.lastChild
29709            ) {
29710             var lc = this.doc.body.lastChild;
29711             // gtx-trans is google translate plugin adding crap.
29712             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29713                 lc = lc.previousSibling;
29714             }
29715             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29716             // if last element is <BR> - then dont do anything.
29717             
29718                 var ns = this.doc.createElement('br');
29719                 this.doc.body.appendChild(ns);
29720                 range = this.doc.createRange();
29721                 range.setStartAfter(ns);
29722                 range.collapse(true);
29723                 var sel = this.win.getSelection();
29724                 sel.removeAllRanges();
29725                 sel.addRange(range);
29726             }
29727         }
29728         
29729         
29730         
29731         this.fireEditorEvent(e);
29732       //  this.updateToolbar();
29733         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29734     },
29735     
29736     fireEditorEvent: function(e)
29737     {
29738         this.owner.fireEvent('editorevent', this, e);
29739     },
29740
29741     insertTag : function(tg)
29742     {
29743         // could be a bit smarter... -> wrap the current selected tRoo..
29744         if (tg.toLowerCase() == 'span' ||
29745             tg.toLowerCase() == 'code' ||
29746             tg.toLowerCase() == 'sup' ||
29747             tg.toLowerCase() == 'sub' 
29748             ) {
29749             
29750             range = this.createRange(this.getSelection());
29751             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29752             wrappingNode.appendChild(range.extractContents());
29753             range.insertNode(wrappingNode);
29754
29755             return;
29756             
29757             
29758             
29759         }
29760         this.execCmd("formatblock",   tg);
29761         this.undoManager.addEvent(); 
29762     },
29763     
29764     insertText : function(txt)
29765     {
29766         
29767         
29768         var range = this.createRange();
29769         range.deleteContents();
29770                //alert(Sender.getAttribute('label'));
29771                
29772         range.insertNode(this.doc.createTextNode(txt));
29773         this.undoManager.addEvent();
29774     } ,
29775     
29776      
29777
29778     /**
29779      * Executes a Midas editor command on the editor document and performs necessary focus and
29780      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29781      * @param {String} cmd The Midas command
29782      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29783      */
29784     relayCmd : function(cmd, value)
29785     {
29786         
29787         switch (cmd) {
29788             case 'justifyleft':
29789             case 'justifyright':
29790             case 'justifycenter':
29791                 // if we are in a cell, then we will adjust the
29792                 var n = this.getParentElement();
29793                 var td = n.closest('td');
29794                 if (td) {
29795                     var bl = Roo.htmleditor.Block.factory(td);
29796                     bl.textAlign = cmd.replace('justify','');
29797                     bl.updateElement();
29798                     this.owner.fireEvent('editorevent', this);
29799                     return;
29800                 }
29801                 this.execCmd('styleWithCSS', true); // 
29802                 break;
29803             case 'bold':
29804             case 'italic':
29805                 // if there is no selection, then we insert, and set the curson inside it..
29806                 this.execCmd('styleWithCSS', false); 
29807                 break;
29808                 
29809         
29810             default:
29811                 break;
29812         }
29813         
29814         
29815         this.win.focus();
29816         this.execCmd(cmd, value);
29817         this.owner.fireEvent('editorevent', this);
29818         //this.updateToolbar();
29819         this.owner.deferFocus();
29820     },
29821
29822     /**
29823      * Executes a Midas editor command directly on the editor document.
29824      * For visual commands, you should use {@link #relayCmd} instead.
29825      * <b>This should only be called after the editor is initialized.</b>
29826      * @param {String} cmd The Midas command
29827      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29828      */
29829     execCmd : function(cmd, value){
29830         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29831         this.syncValue();
29832     },
29833  
29834  
29835    
29836     /**
29837      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29838      * to insert tRoo.
29839      * @param {String} text | dom node.. 
29840      */
29841     insertAtCursor : function(text)
29842     {
29843         
29844         if(!this.activated){
29845             return;
29846         }
29847          
29848         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29849             this.win.focus();
29850             
29851             
29852             // from jquery ui (MIT licenced)
29853             var range, node;
29854             var win = this.win;
29855             
29856             if (win.getSelection && win.getSelection().getRangeAt) {
29857                 
29858                 // delete the existing?
29859                 
29860                 this.createRange(this.getSelection()).deleteContents();
29861                 range = win.getSelection().getRangeAt(0);
29862                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29863                 range.insertNode(node);
29864                 range = range.cloneRange();
29865                 range.collapse(false);
29866                  
29867                 win.getSelection().removeAllRanges();
29868                 win.getSelection().addRange(range);
29869                 
29870                 
29871                 
29872             } else if (win.document.selection && win.document.selection.createRange) {
29873                 // no firefox support
29874                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29875                 win.document.selection.createRange().pasteHTML(txt);
29876             
29877             } else {
29878                 // no firefox support
29879                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29880                 this.execCmd('InsertHTML', txt);
29881             } 
29882             this.syncValue();
29883             
29884             this.deferFocus();
29885         }
29886     },
29887  // private
29888     mozKeyPress : function(e){
29889         if(e.ctrlKey){
29890             var c = e.getCharCode(), cmd;
29891           
29892             if(c > 0){
29893                 c = String.fromCharCode(c).toLowerCase();
29894                 switch(c){
29895                     case 'b':
29896                         cmd = 'bold';
29897                         break;
29898                     case 'i':
29899                         cmd = 'italic';
29900                         break;
29901                     
29902                     case 'u':
29903                         cmd = 'underline';
29904                         break;
29905                     
29906                     //case 'v':
29907                       //  this.cleanUpPaste.defer(100, this);
29908                       //  return;
29909                         
29910                 }
29911                 if(cmd){
29912                     
29913                     this.relayCmd(cmd);
29914                     //this.win.focus();
29915                     //this.execCmd(cmd);
29916                     //this.deferFocus();
29917                     e.preventDefault();
29918                 }
29919                 
29920             }
29921         }
29922     },
29923
29924     // private
29925     fixKeys : function(){ // load time branching for fastest keydown performance
29926         
29927         
29928         if(Roo.isIE){
29929             return function(e){
29930                 var k = e.getKey(), r;
29931                 if(k == e.TAB){
29932                     e.stopEvent();
29933                     r = this.doc.selection.createRange();
29934                     if(r){
29935                         r.collapse(true);
29936                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29937                         this.deferFocus();
29938                     }
29939                     return;
29940                 }
29941                 /// this is handled by Roo.htmleditor.KeyEnter
29942                  /*
29943                 if(k == e.ENTER){
29944                     r = this.doc.selection.createRange();
29945                     if(r){
29946                         var target = r.parentElement();
29947                         if(!target || target.tagName.toLowerCase() != 'li'){
29948                             e.stopEvent();
29949                             r.pasteHTML('<br/>');
29950                             r.collapse(false);
29951                             r.select();
29952                         }
29953                     }
29954                 }
29955                 */
29956                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29957                 //    this.cleanUpPaste.defer(100, this);
29958                 //    return;
29959                 //}
29960                 
29961                 
29962             };
29963         }else if(Roo.isOpera){
29964             return function(e){
29965                 var k = e.getKey();
29966                 if(k == e.TAB){
29967                     e.stopEvent();
29968                     this.win.focus();
29969                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29970                     this.deferFocus();
29971                 }
29972                
29973                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29974                 //    this.cleanUpPaste.defer(100, this);
29975                  //   return;
29976                 //}
29977                 
29978             };
29979         }else if(Roo.isSafari){
29980             return function(e){
29981                 var k = e.getKey();
29982                 
29983                 if(k == e.TAB){
29984                     e.stopEvent();
29985                     this.execCmd('InsertText','\t');
29986                     this.deferFocus();
29987                     return;
29988                 }
29989                  this.mozKeyPress(e);
29990                 
29991                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29992                  //   this.cleanUpPaste.defer(100, this);
29993                  //   return;
29994                // }
29995                 
29996              };
29997         }
29998     }(),
29999     
30000     getAllAncestors: function()
30001     {
30002         var p = this.getSelectedNode();
30003         var a = [];
30004         if (!p) {
30005             a.push(p); // push blank onto stack..
30006             p = this.getParentElement();
30007         }
30008         
30009         
30010         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
30011             a.push(p);
30012             p = p.parentNode;
30013         }
30014         a.push(this.doc.body);
30015         return a;
30016     },
30017     lastSel : false,
30018     lastSelNode : false,
30019     
30020     
30021     getSelection : function() 
30022     {
30023         this.assignDocWin();
30024         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
30025     },
30026     /**
30027      * Select a dom node
30028      * @param {DomElement} node the node to select
30029      */
30030     selectNode : function(node, collapse)
30031     {
30032         var nodeRange = node.ownerDocument.createRange();
30033         try {
30034             nodeRange.selectNode(node);
30035         } catch (e) {
30036             nodeRange.selectNodeContents(node);
30037         }
30038         if (collapse === true) {
30039             nodeRange.collapse(true);
30040         }
30041         //
30042         var s = this.win.getSelection();
30043         s.removeAllRanges();
30044         s.addRange(nodeRange);
30045     },
30046     
30047     getSelectedNode: function() 
30048     {
30049         // this may only work on Gecko!!!
30050         
30051         // should we cache this!!!!
30052         
30053          
30054          
30055         var range = this.createRange(this.getSelection()).cloneRange();
30056         
30057         if (Roo.isIE) {
30058             var parent = range.parentElement();
30059             while (true) {
30060                 var testRange = range.duplicate();
30061                 testRange.moveToElementText(parent);
30062                 if (testRange.inRange(range)) {
30063                     break;
30064                 }
30065                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
30066                     break;
30067                 }
30068                 parent = parent.parentElement;
30069             }
30070             return parent;
30071         }
30072         
30073         // is ancestor a text element.
30074         var ac =  range.commonAncestorContainer;
30075         if (ac.nodeType == 3) {
30076             ac = ac.parentNode;
30077         }
30078         
30079         var ar = ac.childNodes;
30080          
30081         var nodes = [];
30082         var other_nodes = [];
30083         var has_other_nodes = false;
30084         for (var i=0;i<ar.length;i++) {
30085             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
30086                 continue;
30087             }
30088             // fullly contained node.
30089             
30090             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
30091                 nodes.push(ar[i]);
30092                 continue;
30093             }
30094             
30095             // probably selected..
30096             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
30097                 other_nodes.push(ar[i]);
30098                 continue;
30099             }
30100             // outer..
30101             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
30102                 continue;
30103             }
30104             
30105             
30106             has_other_nodes = true;
30107         }
30108         if (!nodes.length && other_nodes.length) {
30109             nodes= other_nodes;
30110         }
30111         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
30112             return false;
30113         }
30114         
30115         return nodes[0];
30116     },
30117     
30118     
30119     createRange: function(sel)
30120     {
30121         // this has strange effects when using with 
30122         // top toolbar - not sure if it's a great idea.
30123         //this.editor.contentWindow.focus();
30124         if (typeof sel != "undefined") {
30125             try {
30126                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
30127             } catch(e) {
30128                 return this.doc.createRange();
30129             }
30130         } else {
30131             return this.doc.createRange();
30132         }
30133     },
30134     getParentElement: function()
30135     {
30136         
30137         this.assignDocWin();
30138         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
30139         
30140         var range = this.createRange(sel);
30141          
30142         try {
30143             var p = range.commonAncestorContainer;
30144             while (p.nodeType == 3) { // text node
30145                 p = p.parentNode;
30146             }
30147             return p;
30148         } catch (e) {
30149             return null;
30150         }
30151     
30152     },
30153     /***
30154      *
30155      * Range intersection.. the hard stuff...
30156      *  '-1' = before
30157      *  '0' = hits..
30158      *  '1' = after.
30159      *         [ -- selected range --- ]
30160      *   [fail]                        [fail]
30161      *
30162      *    basically..
30163      *      if end is before start or  hits it. fail.
30164      *      if start is after end or hits it fail.
30165      *
30166      *   if either hits (but other is outside. - then it's not 
30167      *   
30168      *    
30169      **/
30170     
30171     
30172     // @see http://www.thismuchiknow.co.uk/?p=64.
30173     rangeIntersectsNode : function(range, node)
30174     {
30175         var nodeRange = node.ownerDocument.createRange();
30176         try {
30177             nodeRange.selectNode(node);
30178         } catch (e) {
30179             nodeRange.selectNodeContents(node);
30180         }
30181     
30182         var rangeStartRange = range.cloneRange();
30183         rangeStartRange.collapse(true);
30184     
30185         var rangeEndRange = range.cloneRange();
30186         rangeEndRange.collapse(false);
30187     
30188         var nodeStartRange = nodeRange.cloneRange();
30189         nodeStartRange.collapse(true);
30190     
30191         var nodeEndRange = nodeRange.cloneRange();
30192         nodeEndRange.collapse(false);
30193     
30194         return rangeStartRange.compareBoundaryPoints(
30195                  Range.START_TO_START, nodeEndRange) == -1 &&
30196                rangeEndRange.compareBoundaryPoints(
30197                  Range.START_TO_START, nodeStartRange) == 1;
30198         
30199          
30200     },
30201     rangeCompareNode : function(range, node)
30202     {
30203         var nodeRange = node.ownerDocument.createRange();
30204         try {
30205             nodeRange.selectNode(node);
30206         } catch (e) {
30207             nodeRange.selectNodeContents(node);
30208         }
30209         
30210         
30211         range.collapse(true);
30212     
30213         nodeRange.collapse(true);
30214      
30215         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30216         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30217          
30218         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30219         
30220         var nodeIsBefore   =  ss == 1;
30221         var nodeIsAfter    = ee == -1;
30222         
30223         if (nodeIsBefore && nodeIsAfter) {
30224             return 0; // outer
30225         }
30226         if (!nodeIsBefore && nodeIsAfter) {
30227             return 1; //right trailed.
30228         }
30229         
30230         if (nodeIsBefore && !nodeIsAfter) {
30231             return 2;  // left trailed.
30232         }
30233         // fully contined.
30234         return 3;
30235     },
30236  
30237     cleanWordChars : function(input) {// change the chars to hex code
30238         
30239        var swapCodes  = [ 
30240             [    8211, "&#8211;" ], 
30241             [    8212, "&#8212;" ], 
30242             [    8216,  "'" ],  
30243             [    8217, "'" ],  
30244             [    8220, '"' ],  
30245             [    8221, '"' ],  
30246             [    8226, "*" ],  
30247             [    8230, "..." ]
30248         ]; 
30249         var output = input;
30250         Roo.each(swapCodes, function(sw) { 
30251             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30252             
30253             output = output.replace(swapper, sw[1]);
30254         });
30255         
30256         return output;
30257     },
30258     
30259      
30260     
30261         
30262     
30263     cleanUpChild : function (node)
30264     {
30265         
30266         new Roo.htmleditor.FilterComment({node : node});
30267         new Roo.htmleditor.FilterAttributes({
30268                 node : node,
30269                 attrib_black : this.ablack,
30270                 attrib_clean : this.aclean,
30271                 style_white : this.cwhite,
30272                 style_black : this.cblack
30273         });
30274         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30275         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30276          
30277         
30278     },
30279     
30280     /**
30281      * Clean up MS wordisms...
30282      * @deprecated - use filter directly
30283      */
30284     cleanWord : function(node)
30285     {
30286         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30287         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
30288         
30289     },
30290    
30291     
30292     /**
30293
30294      * @deprecated - use filters
30295      */
30296     cleanTableWidths : function(node)
30297     {
30298         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30299         
30300  
30301     },
30302     
30303      
30304         
30305     applyBlacklists : function()
30306     {
30307         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30308         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30309         
30310         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30311         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30312         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30313         
30314         this.white = [];
30315         this.black = [];
30316         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30317             if (b.indexOf(tag) > -1) {
30318                 return;
30319             }
30320             this.white.push(tag);
30321             
30322         }, this);
30323         
30324         Roo.each(w, function(tag) {
30325             if (b.indexOf(tag) > -1) {
30326                 return;
30327             }
30328             if (this.white.indexOf(tag) > -1) {
30329                 return;
30330             }
30331             this.white.push(tag);
30332             
30333         }, this);
30334         
30335         
30336         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30337             if (w.indexOf(tag) > -1) {
30338                 return;
30339             }
30340             this.black.push(tag);
30341             
30342         }, this);
30343         
30344         Roo.each(b, function(tag) {
30345             if (w.indexOf(tag) > -1) {
30346                 return;
30347             }
30348             if (this.black.indexOf(tag) > -1) {
30349                 return;
30350             }
30351             this.black.push(tag);
30352             
30353         }, this);
30354         
30355         
30356         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30357         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30358         
30359         this.cwhite = [];
30360         this.cblack = [];
30361         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30362             if (b.indexOf(tag) > -1) {
30363                 return;
30364             }
30365             this.cwhite.push(tag);
30366             
30367         }, this);
30368         
30369         Roo.each(w, function(tag) {
30370             if (b.indexOf(tag) > -1) {
30371                 return;
30372             }
30373             if (this.cwhite.indexOf(tag) > -1) {
30374                 return;
30375             }
30376             this.cwhite.push(tag);
30377             
30378         }, this);
30379         
30380         
30381         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30382             if (w.indexOf(tag) > -1) {
30383                 return;
30384             }
30385             this.cblack.push(tag);
30386             
30387         }, this);
30388         
30389         Roo.each(b, function(tag) {
30390             if (w.indexOf(tag) > -1) {
30391                 return;
30392             }
30393             if (this.cblack.indexOf(tag) > -1) {
30394                 return;
30395             }
30396             this.cblack.push(tag);
30397             
30398         }, this);
30399     },
30400     
30401     setStylesheets : function(stylesheets)
30402     {
30403         if(typeof(stylesheets) == 'string'){
30404             Roo.get(this.iframe.contentDocument.head).createChild({
30405                 tag : 'link',
30406                 rel : 'stylesheet',
30407                 type : 'text/css',
30408                 href : stylesheets
30409             });
30410             
30411             return;
30412         }
30413         var _this = this;
30414      
30415         Roo.each(stylesheets, function(s) {
30416             if(!s.length){
30417                 return;
30418             }
30419             
30420             Roo.get(_this.iframe.contentDocument.head).createChild({
30421                 tag : 'link',
30422                 rel : 'stylesheet',
30423                 type : 'text/css',
30424                 href : s
30425             });
30426         });
30427
30428         
30429     },
30430     
30431     
30432     updateLanguage : function()
30433     {
30434         if (!this.iframe || !this.iframe.contentDocument) {
30435             return;
30436         }
30437         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30438     },
30439     
30440     
30441     removeStylesheets : function()
30442     {
30443         var _this = this;
30444         
30445         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30446             s.remove();
30447         });
30448     },
30449     
30450     setStyle : function(style)
30451     {
30452         Roo.get(this.iframe.contentDocument.head).createChild({
30453             tag : 'style',
30454             type : 'text/css',
30455             html : style
30456         });
30457
30458         return;
30459     }
30460     
30461     // hide stuff that is not compatible
30462     /**
30463      * @event blur
30464      * @hide
30465      */
30466     /**
30467      * @event change
30468      * @hide
30469      */
30470     /**
30471      * @event focus
30472      * @hide
30473      */
30474     /**
30475      * @event specialkey
30476      * @hide
30477      */
30478     /**
30479      * @cfg {String} fieldClass @hide
30480      */
30481     /**
30482      * @cfg {String} focusClass @hide
30483      */
30484     /**
30485      * @cfg {String} autoCreate @hide
30486      */
30487     /**
30488      * @cfg {String} inputType @hide
30489      */
30490     /**
30491      * @cfg {String} invalidClass @hide
30492      */
30493     /**
30494      * @cfg {String} invalidText @hide
30495      */
30496     /**
30497      * @cfg {String} msgFx @hide
30498      */
30499     /**
30500      * @cfg {String} validateOnBlur @hide
30501      */
30502 });
30503
30504 Roo.HtmlEditorCore.white = [
30505         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30506         
30507        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30508        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30509        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30510        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30511        'TABLE',   'UL',         'XMP', 
30512        
30513        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30514       'THEAD',   'TR', 
30515      
30516       'DIR', 'MENU', 'OL', 'UL', 'DL',
30517        
30518       'EMBED',  'OBJECT'
30519 ];
30520
30521
30522 Roo.HtmlEditorCore.black = [
30523     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30524         'APPLET', // 
30525         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30526         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30527         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30528         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30529         //'FONT' // CLEAN LATER..
30530         'COLGROUP', 'COL'   // messy tables.
30531         
30532         
30533 ];
30534 Roo.HtmlEditorCore.clean = [ // ?? needed???
30535      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30536 ];
30537 Roo.HtmlEditorCore.tag_remove = [
30538     'FONT', 'TBODY'  
30539 ];
30540 // attributes..
30541
30542 Roo.HtmlEditorCore.ablack = [
30543     'on'
30544 ];
30545     
30546 Roo.HtmlEditorCore.aclean = [ 
30547     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30548 ];
30549
30550 // protocols..
30551 Roo.HtmlEditorCore.pwhite= [
30552         'http',  'https',  'mailto'
30553 ];
30554
30555 // white listed style attributes.
30556 Roo.HtmlEditorCore.cwhite= [
30557       //  'text-align', /// default is to allow most things..
30558       
30559          
30560 //        'font-size'//??
30561 ];
30562
30563 // black listed style attributes.
30564 Roo.HtmlEditorCore.cblack= [
30565       //  'font-size' -- this can be set by the project 
30566 ];
30567
30568
30569
30570
30571     /*
30572  * - LGPL
30573  *
30574  * HtmlEditor
30575  * 
30576  */
30577
30578 /**
30579  * @class Roo.bootstrap.form.HtmlEditor
30580  * @extends Roo.bootstrap.form.TextArea
30581  * Bootstrap HtmlEditor class
30582
30583  * @constructor
30584  * Create a new HtmlEditor
30585  * @param {Object} config The config object
30586  */
30587
30588 Roo.bootstrap.form.HtmlEditor = function(config){
30589     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30590     if (!this.toolbars) {
30591         this.toolbars = [];
30592     }
30593     
30594     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30595     this.addEvents({
30596             /**
30597              * @event initialize
30598              * Fires when the editor is fully initialized (including the iframe)
30599              * @param {HtmlEditor} this
30600              */
30601             initialize: true,
30602             /**
30603              * @event activate
30604              * Fires when the editor is first receives the focus. Any insertion must wait
30605              * until after this event.
30606              * @param {HtmlEditor} this
30607              */
30608             activate: true,
30609              /**
30610              * @event beforesync
30611              * Fires before the textarea is updated with content from the editor iframe. Return false
30612              * to cancel the sync.
30613              * @param {HtmlEditor} this
30614              * @param {String} html
30615              */
30616             beforesync: true,
30617              /**
30618              * @event beforepush
30619              * Fires before the iframe editor is updated with content from the textarea. Return false
30620              * to cancel the push.
30621              * @param {HtmlEditor} this
30622              * @param {String} html
30623              */
30624             beforepush: true,
30625              /**
30626              * @event sync
30627              * Fires when the textarea is updated with content from the editor iframe.
30628              * @param {HtmlEditor} this
30629              * @param {String} html
30630              */
30631             sync: true,
30632              /**
30633              * @event push
30634              * Fires when the iframe editor is updated with content from the textarea.
30635              * @param {HtmlEditor} this
30636              * @param {String} html
30637              */
30638             push: true,
30639              /**
30640              * @event editmodechange
30641              * Fires when the editor switches edit modes
30642              * @param {HtmlEditor} this
30643              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30644              */
30645             editmodechange: true,
30646             /**
30647              * @event editorevent
30648              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30649              * @param {HtmlEditor} this
30650              */
30651             editorevent: true,
30652             /**
30653              * @event firstfocus
30654              * Fires when on first focus - needed by toolbars..
30655              * @param {HtmlEditor} this
30656              */
30657             firstfocus: true,
30658             /**
30659              * @event autosave
30660              * Auto save the htmlEditor value as a file into Events
30661              * @param {HtmlEditor} this
30662              */
30663             autosave: true,
30664             /**
30665              * @event savedpreview
30666              * preview the saved version of htmlEditor
30667              * @param {HtmlEditor} this
30668              */
30669             savedpreview: true
30670         });
30671 };
30672
30673
30674 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30675     
30676     
30677       /**
30678      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30679      */
30680     toolbars : false,
30681     
30682      /**
30683     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30684     */
30685     btns : [],
30686    
30687      /**
30688      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30689      *                        Roo.resizable.
30690      */
30691     resizable : false,
30692      /**
30693      * @cfg {Number} height (in pixels)
30694      */   
30695     height: 300,
30696    /**
30697      * @cfg {Number} width (in pixels)
30698      */   
30699     width: false,
30700     
30701     /**
30702      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30703      * 
30704      */
30705     stylesheets: false,
30706     
30707     // id of frame..
30708     frameId: false,
30709     
30710     // private properties
30711     validationEvent : false,
30712     deferHeight: true,
30713     initialized : false,
30714     activated : false,
30715     
30716     onFocus : Roo.emptyFn,
30717     iframePad:3,
30718     hideMode:'offsets',
30719     
30720     tbContainer : false,
30721     
30722     bodyCls : '',
30723     
30724     toolbarContainer :function() {
30725         return this.wrap.select('.x-html-editor-tb',true).first();
30726     },
30727
30728     /**
30729      * Protected method that will not generally be called directly. It
30730      * is called when the editor creates its toolbar. Override this method if you need to
30731      * add custom toolbar buttons.
30732      * @param {HtmlEditor} editor
30733      */
30734     createToolbar : function(){
30735         Roo.log('renewing');
30736         Roo.log("create toolbars");
30737         
30738         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30739         this.toolbars[0].render(this.toolbarContainer());
30740         
30741         return;
30742         
30743 //        if (!editor.toolbars || !editor.toolbars.length) {
30744 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30745 //        }
30746 //        
30747 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30748 //            editor.toolbars[i] = Roo.factory(
30749 //                    typeof(editor.toolbars[i]) == 'string' ?
30750 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30751 //                Roo.bootstrap.form.HtmlEditor);
30752 //            editor.toolbars[i].init(editor);
30753 //        }
30754     },
30755
30756      
30757     // private
30758     onRender : function(ct, position)
30759     {
30760        // Roo.log("Call onRender: " + this.xtype);
30761         var _t = this;
30762         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30763       
30764         this.wrap = this.inputEl().wrap({
30765             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30766         });
30767         
30768         this.editorcore.onRender(ct, position);
30769          
30770         if (this.resizable) {
30771             this.resizeEl = new Roo.Resizable(this.wrap, {
30772                 pinned : true,
30773                 wrap: true,
30774                 dynamic : true,
30775                 minHeight : this.height,
30776                 height: this.height,
30777                 handles : this.resizable,
30778                 width: this.width,
30779                 listeners : {
30780                     resize : function(r, w, h) {
30781                         _t.onResize(w,h); // -something
30782                     }
30783                 }
30784             });
30785             
30786         }
30787         this.createToolbar(this);
30788        
30789         
30790         if(!this.width && this.resizable){
30791             this.setSize(this.wrap.getSize());
30792         }
30793         if (this.resizeEl) {
30794             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30795             // should trigger onReize..
30796         }
30797         
30798     },
30799
30800     // private
30801     onResize : function(w, h)
30802     {
30803         Roo.log('resize: ' +w + ',' + h );
30804         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30805         var ew = false;
30806         var eh = false;
30807         
30808         if(this.inputEl() ){
30809             if(typeof w == 'number'){
30810                 var aw = w - this.wrap.getFrameWidth('lr');
30811                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30812                 ew = aw;
30813             }
30814             if(typeof h == 'number'){
30815                  var tbh = -11;  // fixme it needs to tool bar size!
30816                 for (var i =0; i < this.toolbars.length;i++) {
30817                     // fixme - ask toolbars for heights?
30818                     tbh += this.toolbars[i].el.getHeight();
30819                     //if (this.toolbars[i].footer) {
30820                     //    tbh += this.toolbars[i].footer.el.getHeight();
30821                     //}
30822                 }
30823               
30824                 
30825                 
30826                 
30827                 
30828                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30829                 ah -= 5; // knock a few pixes off for look..
30830                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30831                 var eh = ah;
30832             }
30833         }
30834         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30835         this.editorcore.onResize(ew,eh);
30836         
30837     },
30838
30839     /**
30840      * Toggles the editor between standard and source edit mode.
30841      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30842      */
30843     toggleSourceEdit : function(sourceEditMode)
30844     {
30845         this.editorcore.toggleSourceEdit(sourceEditMode);
30846         
30847         if(this.editorcore.sourceEditMode){
30848             Roo.log('editor - showing textarea');
30849             
30850 //            Roo.log('in');
30851 //            Roo.log(this.syncValue());
30852             this.syncValue();
30853             this.inputEl().removeClass(['hide', 'x-hidden']);
30854             this.inputEl().dom.removeAttribute('tabIndex');
30855             this.inputEl().focus();
30856         }else{
30857             Roo.log('editor - hiding textarea');
30858 //            Roo.log('out')
30859 //            Roo.log(this.pushValue()); 
30860             this.pushValue();
30861             
30862             this.inputEl().addClass(['hide', 'x-hidden']);
30863             this.inputEl().dom.setAttribute('tabIndex', -1);
30864             //this.deferFocus();
30865         }
30866          
30867         if(this.resizable){
30868             this.setSize(this.wrap.getSize());
30869         }
30870         
30871         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30872     },
30873  
30874     // private (for BoxComponent)
30875     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30876
30877     // private (for BoxComponent)
30878     getResizeEl : function(){
30879         return this.wrap;
30880     },
30881
30882     // private (for BoxComponent)
30883     getPositionEl : function(){
30884         return this.wrap;
30885     },
30886
30887     // private
30888     initEvents : function(){
30889         this.originalValue = this.getValue();
30890     },
30891
30892 //    /**
30893 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30894 //     * @method
30895 //     */
30896 //    markInvalid : Roo.emptyFn,
30897 //    /**
30898 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30899 //     * @method
30900 //     */
30901 //    clearInvalid : Roo.emptyFn,
30902
30903     setValue : function(v){
30904         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30905         this.editorcore.pushValue();
30906     },
30907
30908      
30909     // private
30910     deferFocus : function(){
30911         this.focus.defer(10, this);
30912     },
30913
30914     // doc'ed in Field
30915     focus : function(){
30916         this.editorcore.focus();
30917         
30918     },
30919       
30920
30921     // private
30922     onDestroy : function(){
30923         
30924         
30925         
30926         if(this.rendered){
30927             
30928             for (var i =0; i < this.toolbars.length;i++) {
30929                 // fixme - ask toolbars for heights?
30930                 this.toolbars[i].onDestroy();
30931             }
30932             
30933             this.wrap.dom.innerHTML = '';
30934             this.wrap.remove();
30935         }
30936     },
30937
30938     // private
30939     onFirstFocus : function(){
30940         //Roo.log("onFirstFocus");
30941         this.editorcore.onFirstFocus();
30942          for (var i =0; i < this.toolbars.length;i++) {
30943             this.toolbars[i].onFirstFocus();
30944         }
30945         
30946     },
30947     
30948     // private
30949     syncValue : function()
30950     {   
30951         this.editorcore.syncValue();
30952     },
30953     
30954     pushValue : function()
30955     {   
30956         this.editorcore.pushValue();
30957     }
30958      
30959     
30960     // hide stuff that is not compatible
30961     /**
30962      * @event blur
30963      * @hide
30964      */
30965     /**
30966      * @event change
30967      * @hide
30968      */
30969     /**
30970      * @event focus
30971      * @hide
30972      */
30973     /**
30974      * @event specialkey
30975      * @hide
30976      */
30977     /**
30978      * @cfg {String} fieldClass @hide
30979      */
30980     /**
30981      * @cfg {String} focusClass @hide
30982      */
30983     /**
30984      * @cfg {String} autoCreate @hide
30985      */
30986     /**
30987      * @cfg {String} inputType @hide
30988      */
30989      
30990     /**
30991      * @cfg {String} invalidText @hide
30992      */
30993     /**
30994      * @cfg {String} msgFx @hide
30995      */
30996     /**
30997      * @cfg {String} validateOnBlur @hide
30998      */
30999 });
31000  
31001     
31002    
31003    
31004    
31005       
31006 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
31007 /**
31008  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
31009  * @parent Roo.bootstrap.form.HtmlEditor
31010  * @extends Roo.bootstrap.nav.Simplebar
31011  * Basic Toolbar
31012  * 
31013  * @example
31014  * Usage:
31015  *
31016  new Roo.bootstrap.form.HtmlEditor({
31017     ....
31018     toolbars : [
31019         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
31020             disable : { fonts: 1 , format: 1, ..., ... , ...],
31021             btns : [ .... ]
31022         })
31023     }
31024      
31025  * 
31026  * @cfg {Object} disable List of elements to disable..
31027  * @cfg {Array} btns List of additional buttons.
31028  * 
31029  * 
31030  * NEEDS Extra CSS? 
31031  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
31032  */
31033  
31034 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
31035 {
31036     
31037     Roo.apply(this, config);
31038     
31039     // default disabled, based on 'good practice'..
31040     this.disable = this.disable || {};
31041     Roo.applyIf(this.disable, {
31042         fontSize : true,
31043         colors : true,
31044         specialElements : true
31045     });
31046     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
31047     
31048     this.editor = config.editor;
31049     this.editorcore = config.editor.editorcore;
31050     
31051     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
31052     
31053     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
31054     // dont call parent... till later.
31055 }
31056 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
31057      
31058     bar : true,
31059     
31060     editor : false,
31061     editorcore : false,
31062     
31063     
31064     formats : [
31065         "p" ,  
31066         "h1","h2","h3","h4","h5","h6", 
31067         "pre", "code", 
31068         "abbr", "acronym", "address", "cite", "samp", "var",
31069         'div','span'
31070     ],
31071     
31072     onRender : function(ct, position)
31073     {
31074        // Roo.log("Call onRender: " + this.xtype);
31075         
31076        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
31077        Roo.log(this.el);
31078        this.el.dom.style.marginBottom = '0';
31079        var _this = this;
31080        var editorcore = this.editorcore;
31081        var editor= this.editor;
31082        
31083        var children = [];
31084        var btn = function(id,cmd , toggle, handler, html){
31085        
31086             var  event = toggle ? 'toggle' : 'click';
31087        
31088             var a = {
31089                 size : 'sm',
31090                 xtype: 'Button',
31091                 xns: Roo.bootstrap,
31092                 //glyphicon : id,
31093                 fa: id,
31094                 cmd : id || cmd,
31095                 enableToggle:toggle !== false,
31096                 html : html || '',
31097                 pressed : toggle ? false : null,
31098                 listeners : {}
31099             };
31100             a.listeners[toggle ? 'toggle' : 'click'] = function() {
31101                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
31102             };
31103             children.push(a);
31104             return a;
31105        }
31106        
31107     //    var cb_box = function...
31108         
31109         var style = {
31110                 xtype: 'Button',
31111                 size : 'sm',
31112                 xns: Roo.bootstrap,
31113                 fa : 'font',
31114                 //html : 'submit'
31115                 menu : {
31116                     xtype: 'Menu',
31117                     xns: Roo.bootstrap,
31118                     items:  []
31119                 }
31120         };
31121         Roo.each(this.formats, function(f) {
31122             style.menu.items.push({
31123                 xtype :'MenuItem',
31124                 xns: Roo.bootstrap,
31125                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
31126                 tagname : f,
31127                 listeners : {
31128                     click : function()
31129                     {
31130                         editorcore.insertTag(this.tagname);
31131                         editor.focus();
31132                     }
31133                 }
31134                 
31135             });
31136         });
31137         children.push(style);   
31138         
31139         btn('bold',false,true);
31140         btn('italic',false,true);
31141         btn('align-left', 'justifyleft',true);
31142         btn('align-center', 'justifycenter',true);
31143         btn('align-right' , 'justifyright',true);
31144         btn('link', false, false, function(btn) {
31145             //Roo.log("create link?");
31146             var url = prompt(this.createLinkText, this.defaultLinkValue);
31147             if(url && url != 'http:/'+'/'){
31148                 this.editorcore.relayCmd('createlink', url);
31149             }
31150         }),
31151         btn('list','insertunorderedlist',true);
31152         btn('pencil', false,true, function(btn){
31153                 Roo.log(this);
31154                 this.toggleSourceEdit(btn.pressed);
31155         });
31156         
31157         if (this.editor.btns.length > 0) {
31158             for (var i = 0; i<this.editor.btns.length; i++) {
31159                 children.push(this.editor.btns[i]);
31160             }
31161         }
31162         
31163         /*
31164         var cog = {
31165                 xtype: 'Button',
31166                 size : 'sm',
31167                 xns: Roo.bootstrap,
31168                 glyphicon : 'cog',
31169                 //html : 'submit'
31170                 menu : {
31171                     xtype: 'Menu',
31172                     xns: Roo.bootstrap,
31173                     items:  []
31174                 }
31175         };
31176         
31177         cog.menu.items.push({
31178             xtype :'MenuItem',
31179             xns: Roo.bootstrap,
31180             html : Clean styles,
31181             tagname : f,
31182             listeners : {
31183                 click : function()
31184                 {
31185                     editorcore.insertTag(this.tagname);
31186                     editor.focus();
31187                 }
31188             }
31189             
31190         });
31191        */
31192         
31193          
31194        this.xtype = 'NavSimplebar';
31195         
31196         for(var i=0;i< children.length;i++) {
31197             
31198             this.buttons.add(this.addxtypeChild(children[i]));
31199             
31200         }
31201         
31202         editor.on('editorevent', this.updateToolbar, this);
31203     },
31204     onBtnClick : function(id)
31205     {
31206        this.editorcore.relayCmd(id);
31207        this.editorcore.focus();
31208     },
31209     
31210     /**
31211      * Protected method that will not generally be called directly. It triggers
31212      * a toolbar update by reading the markup state of the current selection in the editor.
31213      */
31214     updateToolbar: function(){
31215
31216         if(!this.editorcore.activated){
31217             this.editor.onFirstFocus(); // is this neeed?
31218             return;
31219         }
31220
31221         var btns = this.buttons; 
31222         var doc = this.editorcore.doc;
31223         btns.get('bold').setActive(doc.queryCommandState('bold'));
31224         btns.get('italic').setActive(doc.queryCommandState('italic'));
31225         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31226         
31227         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31228         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31229         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31230         
31231         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31232         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31233          /*
31234         
31235         var ans = this.editorcore.getAllAncestors();
31236         if (this.formatCombo) {
31237             
31238             
31239             var store = this.formatCombo.store;
31240             this.formatCombo.setValue("");
31241             for (var i =0; i < ans.length;i++) {
31242                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31243                     // select it..
31244                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31245                     break;
31246                 }
31247             }
31248         }
31249         
31250         
31251         
31252         // hides menus... - so this cant be on a menu...
31253         Roo.bootstrap.MenuMgr.hideAll();
31254         */
31255         Roo.bootstrap.menu.Manager.hideAll();
31256         //this.editorsyncValue();
31257     },
31258     onFirstFocus: function() {
31259         this.buttons.each(function(item){
31260            item.enable();
31261         });
31262     },
31263     toggleSourceEdit : function(sourceEditMode){
31264         
31265           
31266         if(sourceEditMode){
31267             Roo.log("disabling buttons");
31268            this.buttons.each( function(item){
31269                 if(item.cmd != 'pencil'){
31270                     item.disable();
31271                 }
31272             });
31273           
31274         }else{
31275             Roo.log("enabling buttons");
31276             if(this.editorcore.initialized){
31277                 this.buttons.each( function(item){
31278                     item.enable();
31279                 });
31280             }
31281             
31282         }
31283         Roo.log("calling toggole on editor");
31284         // tell the editor that it's been pressed..
31285         this.editor.toggleSourceEdit(sourceEditMode);
31286        
31287     }
31288 });
31289
31290
31291
31292
31293  
31294 /*
31295  * - LGPL
31296  */
31297
31298 /**
31299  * @class Roo.bootstrap.form.Markdown
31300  * @extends Roo.bootstrap.form.TextArea
31301  * Bootstrap Showdown editable area
31302  * @cfg {string} content
31303  * 
31304  * @constructor
31305  * Create a new Showdown
31306  */
31307
31308 Roo.bootstrap.form.Markdown = function(config){
31309     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31310    
31311 };
31312
31313 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31314     
31315     editing :false,
31316     
31317     initEvents : function()
31318     {
31319         
31320         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31321         this.markdownEl = this.el.createChild({
31322             cls : 'roo-markdown-area'
31323         });
31324         this.inputEl().addClass('d-none');
31325         if (this.getValue() == '') {
31326             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31327             
31328         } else {
31329             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31330         }
31331         this.markdownEl.on('click', this.toggleTextEdit, this);
31332         this.on('blur', this.toggleTextEdit, this);
31333         this.on('specialkey', this.resizeTextArea, this);
31334     },
31335     
31336     toggleTextEdit : function()
31337     {
31338         var sh = this.markdownEl.getHeight();
31339         this.inputEl().addClass('d-none');
31340         this.markdownEl.addClass('d-none');
31341         if (!this.editing) {
31342             // show editor?
31343             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31344             this.inputEl().removeClass('d-none');
31345             this.inputEl().focus();
31346             this.editing = true;
31347             return;
31348         }
31349         // show showdown...
31350         this.updateMarkdown();
31351         this.markdownEl.removeClass('d-none');
31352         this.editing = false;
31353         return;
31354     },
31355     updateMarkdown : function()
31356     {
31357         if (this.getValue() == '') {
31358             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31359             return;
31360         }
31361  
31362         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31363     },
31364     
31365     resizeTextArea: function () {
31366         
31367         var sh = 100;
31368         Roo.log([sh, this.getValue().split("\n").length * 30]);
31369         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31370     },
31371     setValue : function(val)
31372     {
31373         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31374         if (!this.editing) {
31375             this.updateMarkdown();
31376         }
31377         
31378     },
31379     focus : function()
31380     {
31381         if (!this.editing) {
31382             this.toggleTextEdit();
31383         }
31384         
31385     }
31386
31387
31388 });/*
31389  * Based on:
31390  * Ext JS Library 1.1.1
31391  * Copyright(c) 2006-2007, Ext JS, LLC.
31392  *
31393  * Originally Released Under LGPL - original licence link has changed is not relivant.
31394  *
31395  * Fork - LGPL
31396  * <script type="text/javascript">
31397  */
31398  
31399 /**
31400  * @class Roo.bootstrap.PagingToolbar
31401  * @extends Roo.bootstrap.nav.Simplebar
31402  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31403  * @constructor
31404  * Create a new PagingToolbar
31405  * @param {Object} config The config object
31406  * @param {Roo.data.Store} store
31407  */
31408 Roo.bootstrap.PagingToolbar = function(config)
31409 {
31410     // old args format still supported... - xtype is prefered..
31411         // created from xtype...
31412     
31413     this.ds = config.dataSource;
31414     
31415     if (config.store && !this.ds) {
31416         this.store= Roo.factory(config.store, Roo.data);
31417         this.ds = this.store;
31418         this.ds.xmodule = this.xmodule || false;
31419     }
31420     
31421     this.toolbarItems = [];
31422     if (config.items) {
31423         this.toolbarItems = config.items;
31424     }
31425     
31426     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31427     
31428     this.cursor = 0;
31429     
31430     if (this.ds) { 
31431         this.bind(this.ds);
31432     }
31433     
31434     if (Roo.bootstrap.version == 4) {
31435         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31436     } else {
31437         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31438     }
31439     
31440 };
31441
31442 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31443     /**
31444      * @cfg {Roo.bootstrap.Button} buttons[]
31445      * Buttons for the toolbar
31446      */
31447      /**
31448      * @cfg {Roo.data.Store} store
31449      * The underlying data store providing the paged data
31450      */
31451     /**
31452      * @cfg {String/HTMLElement/Element} container
31453      * container The id or element that will contain the toolbar
31454      */
31455     /**
31456      * @cfg {Boolean} displayInfo
31457      * True to display the displayMsg (defaults to false)
31458      */
31459     /**
31460      * @cfg {Number} pageSize
31461      * The number of records to display per page (defaults to 20)
31462      */
31463     pageSize: 20,
31464     /**
31465      * @cfg {String} displayMsg
31466      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31467      */
31468     displayMsg : 'Displaying {0} - {1} of {2}',
31469     /**
31470      * @cfg {String} emptyMsg
31471      * The message to display when no records are found (defaults to "No data to display")
31472      */
31473     emptyMsg : 'No data to display',
31474     /**
31475      * Customizable piece of the default paging text (defaults to "Page")
31476      * @type String
31477      */
31478     beforePageText : "Page",
31479     /**
31480      * Customizable piece of the default paging text (defaults to "of %0")
31481      * @type String
31482      */
31483     afterPageText : "of {0}",
31484     /**
31485      * Customizable piece of the default paging text (defaults to "First Page")
31486      * @type String
31487      */
31488     firstText : "First Page",
31489     /**
31490      * Customizable piece of the default paging text (defaults to "Previous Page")
31491      * @type String
31492      */
31493     prevText : "Previous Page",
31494     /**
31495      * Customizable piece of the default paging text (defaults to "Next Page")
31496      * @type String
31497      */
31498     nextText : "Next Page",
31499     /**
31500      * Customizable piece of the default paging text (defaults to "Last Page")
31501      * @type String
31502      */
31503     lastText : "Last Page",
31504     /**
31505      * Customizable piece of the default paging text (defaults to "Refresh")
31506      * @type String
31507      */
31508     refreshText : "Refresh",
31509
31510     buttons : false,
31511     // private
31512     onRender : function(ct, position) 
31513     {
31514         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31515         this.navgroup.parentId = this.id;
31516         this.navgroup.onRender(this.el, null);
31517         // add the buttons to the navgroup
31518         
31519         if(this.displayInfo){
31520             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31521             this.displayEl = this.el.select('.x-paging-info', true).first();
31522 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31523 //            this.displayEl = navel.el.select('span',true).first();
31524         }
31525         
31526         var _this = this;
31527         
31528         if(this.buttons){
31529             Roo.each(_this.buttons, function(e){ // this might need to use render????
31530                Roo.factory(e).render(_this.el);
31531             });
31532         }
31533             
31534         Roo.each(_this.toolbarItems, function(e) {
31535             _this.navgroup.addItem(e);
31536         });
31537         
31538         
31539         this.first = this.navgroup.addItem({
31540             tooltip: this.firstText,
31541             cls: "prev btn-outline-secondary",
31542             html : ' <i class="fa fa-step-backward"></i>',
31543             disabled: true,
31544             preventDefault: true,
31545             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31546         });
31547         
31548         this.prev =  this.navgroup.addItem({
31549             tooltip: this.prevText,
31550             cls: "prev btn-outline-secondary",
31551             html : ' <i class="fa fa-backward"></i>',
31552             disabled: true,
31553             preventDefault: true,
31554             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31555         });
31556     //this.addSeparator();
31557         
31558         
31559         var field = this.navgroup.addItem( {
31560             tagtype : 'span',
31561             cls : 'x-paging-position  btn-outline-secondary',
31562              disabled: true,
31563             html : this.beforePageText  +
31564                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31565                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31566          } ); //?? escaped?
31567         
31568         this.field = field.el.select('input', true).first();
31569         this.field.on("keydown", this.onPagingKeydown, this);
31570         this.field.on("focus", function(){this.dom.select();});
31571     
31572     
31573         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31574         //this.field.setHeight(18);
31575         //this.addSeparator();
31576         this.next = this.navgroup.addItem({
31577             tooltip: this.nextText,
31578             cls: "next btn-outline-secondary",
31579             html : ' <i class="fa fa-forward"></i>',
31580             disabled: true,
31581             preventDefault: true,
31582             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31583         });
31584         this.last = this.navgroup.addItem({
31585             tooltip: this.lastText,
31586             html : ' <i class="fa fa-step-forward"></i>',
31587             cls: "next btn-outline-secondary",
31588             disabled: true,
31589             preventDefault: true,
31590             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31591         });
31592     //this.addSeparator();
31593         this.loading = this.navgroup.addItem({
31594             tooltip: this.refreshText,
31595             cls: "btn-outline-secondary",
31596             html : ' <i class="fa fa-refresh"></i>',
31597             preventDefault: true,
31598             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31599         });
31600         
31601     },
31602
31603     // private
31604     updateInfo : function(){
31605         if(this.displayEl){
31606             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31607             var msg = count == 0 ?
31608                 this.emptyMsg :
31609                 String.format(
31610                     this.displayMsg,
31611                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31612                 );
31613             this.displayEl.update(msg);
31614         }
31615     },
31616
31617     // private
31618     onLoad : function(ds, r, o)
31619     {
31620         this.cursor = o.params && o.params.start ? o.params.start : 0;
31621         
31622         var d = this.getPageData(),
31623             ap = d.activePage,
31624             ps = d.pages;
31625         
31626         
31627         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31628         this.field.dom.value = ap;
31629         this.first.setDisabled(ap == 1);
31630         this.prev.setDisabled(ap == 1);
31631         this.next.setDisabled(ap == ps);
31632         this.last.setDisabled(ap == ps);
31633         this.loading.enable();
31634         this.updateInfo();
31635     },
31636
31637     // private
31638     getPageData : function(){
31639         var total = this.ds.getTotalCount();
31640         return {
31641             total : total,
31642             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31643             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31644         };
31645     },
31646
31647     // private
31648     onLoadError : function(proxy, o){
31649         this.loading.enable();
31650         if (this.ds.events.loadexception.listeners.length  < 2) {
31651             // nothing has been assigned to loadexception except this...
31652             // so 
31653             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31654
31655         }
31656     },
31657
31658     // private
31659     onPagingKeydown : function(e){
31660         var k = e.getKey();
31661         var d = this.getPageData();
31662         if(k == e.RETURN){
31663             var v = this.field.dom.value, pageNum;
31664             if(!v || isNaN(pageNum = parseInt(v, 10))){
31665                 this.field.dom.value = d.activePage;
31666                 return;
31667             }
31668             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31669             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31670             e.stopEvent();
31671         }
31672         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))
31673         {
31674           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31675           this.field.dom.value = pageNum;
31676           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31677           e.stopEvent();
31678         }
31679         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31680         {
31681           var v = this.field.dom.value, pageNum; 
31682           var increment = (e.shiftKey) ? 10 : 1;
31683           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31684                 increment *= -1;
31685           }
31686           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31687             this.field.dom.value = d.activePage;
31688             return;
31689           }
31690           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31691           {
31692             this.field.dom.value = parseInt(v, 10) + increment;
31693             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31694             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31695           }
31696           e.stopEvent();
31697         }
31698     },
31699
31700     // private
31701     beforeLoad : function(){
31702         if(this.loading){
31703             this.loading.disable();
31704         }
31705     },
31706
31707     // private
31708     onClick : function(which){
31709         
31710         var ds = this.ds;
31711         if (!ds) {
31712             return;
31713         }
31714         
31715         switch(which){
31716             case "first":
31717                 ds.load({params:{start: 0, limit: this.pageSize}});
31718             break;
31719             case "prev":
31720                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31721             break;
31722             case "next":
31723                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31724             break;
31725             case "last":
31726                 var total = ds.getTotalCount();
31727                 var extra = total % this.pageSize;
31728                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31729                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31730             break;
31731             case "refresh":
31732                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31733             break;
31734         }
31735     },
31736
31737     /**
31738      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31739      * @param {Roo.data.Store} store The data store to unbind
31740      */
31741     unbind : function(ds){
31742         ds.un("beforeload", this.beforeLoad, this);
31743         ds.un("load", this.onLoad, this);
31744         ds.un("loadexception", this.onLoadError, this);
31745         ds.un("remove", this.updateInfo, this);
31746         ds.un("add", this.updateInfo, this);
31747         this.ds = undefined;
31748     },
31749
31750     /**
31751      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31752      * @param {Roo.data.Store} store The data store to bind
31753      */
31754     bind : function(ds){
31755         ds.on("beforeload", this.beforeLoad, this);
31756         ds.on("load", this.onLoad, this);
31757         ds.on("loadexception", this.onLoadError, this);
31758         ds.on("remove", this.updateInfo, this);
31759         ds.on("add", this.updateInfo, this);
31760         this.ds = ds;
31761     }
31762 });/*
31763  * - LGPL
31764  *
31765  * element
31766  * 
31767  */
31768
31769 /**
31770  * @class Roo.bootstrap.MessageBar
31771  * @extends Roo.bootstrap.Component
31772  * Bootstrap MessageBar class
31773  * @cfg {String} html contents of the MessageBar
31774  * @cfg {String} weight (info | success | warning | danger) default info
31775  * @cfg {String} beforeClass insert the bar before the given class
31776  * @cfg {Boolean} closable (true | false) default false
31777  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31778  * 
31779  * @constructor
31780  * Create a new Element
31781  * @param {Object} config The config object
31782  */
31783
31784 Roo.bootstrap.MessageBar = function(config){
31785     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31786 };
31787
31788 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31789     
31790     html: '',
31791     weight: 'info',
31792     closable: false,
31793     fixed: false,
31794     beforeClass: 'bootstrap-sticky-wrap',
31795     
31796     getAutoCreate : function(){
31797         
31798         var cfg = {
31799             tag: 'div',
31800             cls: 'alert alert-dismissable alert-' + this.weight,
31801             cn: [
31802                 {
31803                     tag: 'span',
31804                     cls: 'message',
31805                     html: this.html || ''
31806                 }
31807             ]
31808         };
31809         
31810         if(this.fixed){
31811             cfg.cls += ' alert-messages-fixed';
31812         }
31813         
31814         if(this.closable){
31815             cfg.cn.push({
31816                 tag: 'button',
31817                 cls: 'close',
31818                 html: 'x'
31819             });
31820         }
31821         
31822         return cfg;
31823     },
31824     
31825     onRender : function(ct, position)
31826     {
31827         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31828         
31829         if(!this.el){
31830             var cfg = Roo.apply({},  this.getAutoCreate());
31831             cfg.id = Roo.id();
31832             
31833             if (this.cls) {
31834                 cfg.cls += ' ' + this.cls;
31835             }
31836             if (this.style) {
31837                 cfg.style = this.style;
31838             }
31839             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31840             
31841             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31842         }
31843         
31844         this.el.select('>button.close').on('click', this.hide, this);
31845         
31846     },
31847     
31848     show : function()
31849     {
31850         if (!this.rendered) {
31851             this.render();
31852         }
31853         
31854         this.el.show();
31855         
31856         this.fireEvent('show', this);
31857         
31858     },
31859     
31860     hide : function()
31861     {
31862         if (!this.rendered) {
31863             this.render();
31864         }
31865         
31866         this.el.hide();
31867         
31868         this.fireEvent('hide', this);
31869     },
31870     
31871     update : function()
31872     {
31873 //        var e = this.el.dom.firstChild;
31874 //        
31875 //        if(this.closable){
31876 //            e = e.nextSibling;
31877 //        }
31878 //        
31879 //        e.data = this.html || '';
31880
31881         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31882     }
31883    
31884 });
31885
31886  
31887
31888      /*
31889  * - LGPL
31890  *
31891  * Graph
31892  * 
31893  */
31894
31895
31896 /**
31897  * @class Roo.bootstrap.Graph
31898  * @extends Roo.bootstrap.Component
31899  * Bootstrap Graph class
31900 > Prameters
31901  -sm {number} sm 4
31902  -md {number} md 5
31903  @cfg {String} graphtype  bar | vbar | pie
31904  @cfg {number} g_x coodinator | centre x (pie)
31905  @cfg {number} g_y coodinator | centre y (pie)
31906  @cfg {number} g_r radius (pie)
31907  @cfg {number} g_height height of the chart (respected by all elements in the set)
31908  @cfg {number} g_width width of the chart (respected by all elements in the set)
31909  @cfg {Object} title The title of the chart
31910     
31911  -{Array}  values
31912  -opts (object) options for the chart 
31913      o {
31914      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31915      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31916      o vgutter (number)
31917      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.
31918      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31919      o to
31920      o stretch (boolean)
31921      o }
31922  -opts (object) options for the pie
31923      o{
31924      o cut
31925      o startAngle (number)
31926      o endAngle (number)
31927      } 
31928  *
31929  * @constructor
31930  * Create a new Input
31931  * @param {Object} config The config object
31932  */
31933
31934 Roo.bootstrap.Graph = function(config){
31935     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31936     
31937     this.addEvents({
31938         // img events
31939         /**
31940          * @event click
31941          * The img click event for the img.
31942          * @param {Roo.EventObject} e
31943          */
31944         "click" : true
31945     });
31946 };
31947
31948 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31949     
31950     sm: 4,
31951     md: 5,
31952     graphtype: 'bar',
31953     g_height: 250,
31954     g_width: 400,
31955     g_x: 50,
31956     g_y: 50,
31957     g_r: 30,
31958     opts:{
31959         //g_colors: this.colors,
31960         g_type: 'soft',
31961         g_gutter: '20%'
31962
31963     },
31964     title : false,
31965
31966     getAutoCreate : function(){
31967         
31968         var cfg = {
31969             tag: 'div',
31970             html : null
31971         };
31972         
31973         
31974         return  cfg;
31975     },
31976
31977     onRender : function(ct,position){
31978         
31979         
31980         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31981         
31982         if (typeof(Raphael) == 'undefined') {
31983             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31984             return;
31985         }
31986         
31987         this.raphael = Raphael(this.el.dom);
31988         
31989                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31990                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31991                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31992                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31993                 /*
31994                 r.text(160, 10, "Single Series Chart").attr(txtattr);
31995                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31996                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31997                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31998                 
31999                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
32000                 r.barchart(330, 10, 300, 220, data1);
32001                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
32002                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
32003                 */
32004                 
32005                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32006                 // r.barchart(30, 30, 560, 250,  xdata, {
32007                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
32008                 //     axis : "0 0 1 1",
32009                 //     axisxlabels :  xdata
32010                 //     //yvalues : cols,
32011                    
32012                 // });
32013 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32014 //        
32015 //        this.load(null,xdata,{
32016 //                axis : "0 0 1 1",
32017 //                axisxlabels :  xdata
32018 //                });
32019
32020     },
32021
32022     load : function(graphtype,xdata,opts)
32023     {
32024         this.raphael.clear();
32025         if(!graphtype) {
32026             graphtype = this.graphtype;
32027         }
32028         if(!opts){
32029             opts = this.opts;
32030         }
32031         var r = this.raphael,
32032             fin = function () {
32033                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
32034             },
32035             fout = function () {
32036                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
32037             },
32038             pfin = function() {
32039                 this.sector.stop();
32040                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
32041
32042                 if (this.label) {
32043                     this.label[0].stop();
32044                     this.label[0].attr({ r: 7.5 });
32045                     this.label[1].attr({ "font-weight": 800 });
32046                 }
32047             },
32048             pfout = function() {
32049                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
32050
32051                 if (this.label) {
32052                     this.label[0].animate({ r: 5 }, 500, "bounce");
32053                     this.label[1].attr({ "font-weight": 400 });
32054                 }
32055             };
32056
32057         switch(graphtype){
32058             case 'bar':
32059                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32060                 break;
32061             case 'hbar':
32062                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32063                 break;
32064             case 'pie':
32065 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
32066 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
32067 //            
32068                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
32069                 
32070                 break;
32071
32072         }
32073         
32074         if(this.title){
32075             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
32076         }
32077         
32078     },
32079     
32080     setTitle: function(o)
32081     {
32082         this.title = o;
32083     },
32084     
32085     initEvents: function() {
32086         
32087         if(!this.href){
32088             this.el.on('click', this.onClick, this);
32089         }
32090     },
32091     
32092     onClick : function(e)
32093     {
32094         Roo.log('img onclick');
32095         this.fireEvent('click', this, e);
32096     }
32097    
32098 });
32099
32100  
32101 Roo.bootstrap.dash = {};/*
32102  * - LGPL
32103  *
32104  * numberBox
32105  * 
32106  */
32107 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32108
32109 /**
32110  * @class Roo.bootstrap.dash.NumberBox
32111  * @extends Roo.bootstrap.Component
32112  * Bootstrap NumberBox class
32113  * @cfg {String} headline Box headline
32114  * @cfg {String} content Box content
32115  * @cfg {String} icon Box icon
32116  * @cfg {String} footer Footer text
32117  * @cfg {String} fhref Footer href
32118  * 
32119  * @constructor
32120  * Create a new NumberBox
32121  * @param {Object} config The config object
32122  */
32123
32124
32125 Roo.bootstrap.dash.NumberBox = function(config){
32126     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
32127     
32128 };
32129
32130 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
32131     
32132     headline : '',
32133     content : '',
32134     icon : '',
32135     footer : '',
32136     fhref : '',
32137     ficon : '',
32138     
32139     getAutoCreate : function(){
32140         
32141         var cfg = {
32142             tag : 'div',
32143             cls : 'small-box ',
32144             cn : [
32145                 {
32146                     tag : 'div',
32147                     cls : 'inner',
32148                     cn :[
32149                         {
32150                             tag : 'h3',
32151                             cls : 'roo-headline',
32152                             html : this.headline
32153                         },
32154                         {
32155                             tag : 'p',
32156                             cls : 'roo-content',
32157                             html : this.content
32158                         }
32159                     ]
32160                 }
32161             ]
32162         };
32163         
32164         if(this.icon){
32165             cfg.cn.push({
32166                 tag : 'div',
32167                 cls : 'icon',
32168                 cn :[
32169                     {
32170                         tag : 'i',
32171                         cls : 'ion ' + this.icon
32172                     }
32173                 ]
32174             });
32175         }
32176         
32177         if(this.footer){
32178             var footer = {
32179                 tag : 'a',
32180                 cls : 'small-box-footer',
32181                 href : this.fhref || '#',
32182                 html : this.footer
32183             };
32184             
32185             cfg.cn.push(footer);
32186             
32187         }
32188         
32189         return  cfg;
32190     },
32191
32192     onRender : function(ct,position){
32193         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32194
32195
32196        
32197                 
32198     },
32199
32200     setHeadline: function (value)
32201     {
32202         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32203     },
32204     
32205     setFooter: function (value, href)
32206     {
32207         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32208         
32209         if(href){
32210             this.el.select('a.small-box-footer',true).first().attr('href', href);
32211         }
32212         
32213     },
32214
32215     setContent: function (value)
32216     {
32217         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32218     },
32219
32220     initEvents: function() 
32221     {   
32222         
32223     }
32224     
32225 });
32226
32227  
32228 /*
32229  * - LGPL
32230  *
32231  * TabBox
32232  * 
32233  */
32234 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32235
32236 /**
32237  * @class Roo.bootstrap.dash.TabBox
32238  * @extends Roo.bootstrap.Component
32239  * @children Roo.bootstrap.dash.TabPane
32240  * Bootstrap TabBox class
32241  * @cfg {String} title Title of the TabBox
32242  * @cfg {String} icon Icon of the TabBox
32243  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32244  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32245  * 
32246  * @constructor
32247  * Create a new TabBox
32248  * @param {Object} config The config object
32249  */
32250
32251
32252 Roo.bootstrap.dash.TabBox = function(config){
32253     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32254     this.addEvents({
32255         // raw events
32256         /**
32257          * @event addpane
32258          * When a pane is added
32259          * @param {Roo.bootstrap.dash.TabPane} pane
32260          */
32261         "addpane" : true,
32262         /**
32263          * @event activatepane
32264          * When a pane is activated
32265          * @param {Roo.bootstrap.dash.TabPane} pane
32266          */
32267         "activatepane" : true
32268         
32269          
32270     });
32271     
32272     this.panes = [];
32273 };
32274
32275 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32276
32277     title : '',
32278     icon : false,
32279     showtabs : true,
32280     tabScrollable : false,
32281     
32282     getChildContainer : function()
32283     {
32284         return this.el.select('.tab-content', true).first();
32285     },
32286     
32287     getAutoCreate : function(){
32288         
32289         var header = {
32290             tag: 'li',
32291             cls: 'pull-left header',
32292             html: this.title,
32293             cn : []
32294         };
32295         
32296         if(this.icon){
32297             header.cn.push({
32298                 tag: 'i',
32299                 cls: 'fa ' + this.icon
32300             });
32301         }
32302         
32303         var h = {
32304             tag: 'ul',
32305             cls: 'nav nav-tabs pull-right',
32306             cn: [
32307                 header
32308             ]
32309         };
32310         
32311         if(this.tabScrollable){
32312             h = {
32313                 tag: 'div',
32314                 cls: 'tab-header',
32315                 cn: [
32316                     {
32317                         tag: 'ul',
32318                         cls: 'nav nav-tabs pull-right',
32319                         cn: [
32320                             header
32321                         ]
32322                     }
32323                 ]
32324             };
32325         }
32326         
32327         var cfg = {
32328             tag: 'div',
32329             cls: 'nav-tabs-custom',
32330             cn: [
32331                 h,
32332                 {
32333                     tag: 'div',
32334                     cls: 'tab-content no-padding',
32335                     cn: []
32336                 }
32337             ]
32338         };
32339
32340         return  cfg;
32341     },
32342     initEvents : function()
32343     {
32344         //Roo.log('add add pane handler');
32345         this.on('addpane', this.onAddPane, this);
32346     },
32347      /**
32348      * Updates the box title
32349      * @param {String} html to set the title to.
32350      */
32351     setTitle : function(value)
32352     {
32353         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32354     },
32355     onAddPane : function(pane)
32356     {
32357         this.panes.push(pane);
32358         //Roo.log('addpane');
32359         //Roo.log(pane);
32360         // tabs are rendere left to right..
32361         if(!this.showtabs){
32362             return;
32363         }
32364         
32365         var ctr = this.el.select('.nav-tabs', true).first();
32366          
32367          
32368         var existing = ctr.select('.nav-tab',true);
32369         var qty = existing.getCount();;
32370         
32371         
32372         var tab = ctr.createChild({
32373             tag : 'li',
32374             cls : 'nav-tab' + (qty ? '' : ' active'),
32375             cn : [
32376                 {
32377                     tag : 'a',
32378                     href:'#',
32379                     html : pane.title
32380                 }
32381             ]
32382         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32383         pane.tab = tab;
32384         
32385         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32386         if (!qty) {
32387             pane.el.addClass('active');
32388         }
32389         
32390                 
32391     },
32392     onTabClick : function(ev,un,ob,pane)
32393     {
32394         //Roo.log('tab - prev default');
32395         ev.preventDefault();
32396         
32397         
32398         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32399         pane.tab.addClass('active');
32400         //Roo.log(pane.title);
32401         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32402         // technically we should have a deactivate event.. but maybe add later.
32403         // and it should not de-activate the selected tab...
32404         this.fireEvent('activatepane', pane);
32405         pane.el.addClass('active');
32406         pane.fireEvent('activate');
32407         
32408         
32409     },
32410     
32411     getActivePane : function()
32412     {
32413         var r = false;
32414         Roo.each(this.panes, function(p) {
32415             if(p.el.hasClass('active')){
32416                 r = p;
32417                 return false;
32418             }
32419             
32420             return;
32421         });
32422         
32423         return r;
32424     }
32425     
32426     
32427 });
32428
32429  
32430 /*
32431  * - LGPL
32432  *
32433  * Tab pane
32434  * 
32435  */
32436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32437 /**
32438  * @class Roo.bootstrap.TabPane
32439  * @extends Roo.bootstrap.Component
32440  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32441  * Bootstrap TabPane class
32442  * @cfg {Boolean} active (false | true) Default false
32443  * @cfg {String} title title of panel
32444
32445  * 
32446  * @constructor
32447  * Create a new TabPane
32448  * @param {Object} config The config object
32449  */
32450
32451 Roo.bootstrap.dash.TabPane = function(config){
32452     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32453     
32454     this.addEvents({
32455         // raw events
32456         /**
32457          * @event activate
32458          * When a pane is activated
32459          * @param {Roo.bootstrap.dash.TabPane} pane
32460          */
32461         "activate" : true
32462          
32463     });
32464 };
32465
32466 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32467     
32468     active : false,
32469     title : '',
32470     
32471     // the tabBox that this is attached to.
32472     tab : false,
32473      
32474     getAutoCreate : function() 
32475     {
32476         var cfg = {
32477             tag: 'div',
32478             cls: 'tab-pane'
32479         };
32480         
32481         if(this.active){
32482             cfg.cls += ' active';
32483         }
32484         
32485         return cfg;
32486     },
32487     initEvents  : function()
32488     {
32489         //Roo.log('trigger add pane handler');
32490         this.parent().fireEvent('addpane', this)
32491     },
32492     
32493      /**
32494      * Updates the tab title 
32495      * @param {String} html to set the title to.
32496      */
32497     setTitle: function(str)
32498     {
32499         if (!this.tab) {
32500             return;
32501         }
32502         this.title = str;
32503         this.tab.select('a', true).first().dom.innerHTML = str;
32504         
32505     }
32506     
32507     
32508     
32509 });
32510
32511  
32512
32513
32514  /*
32515  * - LGPL
32516  *
32517  * Tooltip
32518  * 
32519  */
32520
32521 /**
32522  * @class Roo.bootstrap.Tooltip
32523  * Bootstrap Tooltip class
32524  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32525  * to determine which dom element triggers the tooltip.
32526  * 
32527  * It needs to add support for additional attributes like tooltip-position
32528  * 
32529  * @constructor
32530  * Create a new Toolti
32531  * @param {Object} config The config object
32532  */
32533
32534 Roo.bootstrap.Tooltip = function(config){
32535     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32536     
32537     this.alignment = Roo.bootstrap.Tooltip.alignment;
32538     
32539     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32540         this.alignment = config.alignment;
32541     }
32542     
32543 };
32544
32545 Roo.apply(Roo.bootstrap.Tooltip, {
32546     /**
32547      * @function init initialize tooltip monitoring.
32548      * @static
32549      */
32550     currentEl : false,
32551     currentTip : false,
32552     currentRegion : false,
32553     
32554     //  init : delay?
32555     
32556     init : function()
32557     {
32558         Roo.get(document).on('mouseover', this.enter ,this);
32559         Roo.get(document).on('mouseout', this.leave, this);
32560          
32561         
32562         this.currentTip = new Roo.bootstrap.Tooltip();
32563     },
32564     
32565     enter : function(ev)
32566     {
32567         var dom = ev.getTarget();
32568         
32569         //Roo.log(['enter',dom]);
32570         var el = Roo.fly(dom);
32571         if (this.currentEl) {
32572             //Roo.log(dom);
32573             //Roo.log(this.currentEl);
32574             //Roo.log(this.currentEl.contains(dom));
32575             if (this.currentEl == el) {
32576                 return;
32577             }
32578             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32579                 return;
32580             }
32581
32582         }
32583         
32584         if (this.currentTip.el) {
32585             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32586         }    
32587         //Roo.log(ev);
32588         
32589         if(!el || el.dom == document){
32590             return;
32591         }
32592         
32593         var bindEl = el; 
32594         var pel = false;
32595         if (!el.attr('tooltip')) {
32596             pel = el.findParent("[tooltip]");
32597             if (pel) {
32598                 bindEl = Roo.get(pel);
32599             }
32600         }
32601         
32602        
32603         
32604         // you can not look for children, as if el is the body.. then everythign is the child..
32605         if (!pel && !el.attr('tooltip')) { //
32606             if (!el.select("[tooltip]").elements.length) {
32607                 return;
32608             }
32609             // is the mouse over this child...?
32610             bindEl = el.select("[tooltip]").first();
32611             var xy = ev.getXY();
32612             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32613                 //Roo.log("not in region.");
32614                 return;
32615             }
32616             //Roo.log("child element over..");
32617             
32618         }
32619         this.currentEl = el;
32620         this.currentTip.bind(bindEl);
32621         this.currentRegion = Roo.lib.Region.getRegion(dom);
32622         this.currentTip.enter();
32623         
32624     },
32625     leave : function(ev)
32626     {
32627         var dom = ev.getTarget();
32628         //Roo.log(['leave',dom]);
32629         if (!this.currentEl) {
32630             return;
32631         }
32632         
32633         
32634         if (dom != this.currentEl.dom) {
32635             return;
32636         }
32637         var xy = ev.getXY();
32638         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32639             return;
32640         }
32641         // only activate leave if mouse cursor is outside... bounding box..
32642         
32643         
32644         
32645         
32646         if (this.currentTip) {
32647             this.currentTip.leave();
32648         }
32649         //Roo.log('clear currentEl');
32650         this.currentEl = false;
32651         
32652         
32653     },
32654     alignment : {
32655         'left' : ['r-l', [-2,0], 'right'],
32656         'right' : ['l-r', [2,0], 'left'],
32657         'bottom' : ['t-b', [0,2], 'top'],
32658         'top' : [ 'b-t', [0,-2], 'bottom']
32659     }
32660     
32661 });
32662
32663
32664 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32665     
32666     
32667     bindEl : false,
32668     
32669     delay : null, // can be { show : 300 , hide: 500}
32670     
32671     timeout : null,
32672     
32673     hoverState : null, //???
32674     
32675     placement : 'bottom', 
32676     
32677     alignment : false,
32678     
32679     getAutoCreate : function(){
32680     
32681         var cfg = {
32682            cls : 'tooltip',   
32683            role : 'tooltip',
32684            cn : [
32685                 {
32686                     cls : 'tooltip-arrow arrow'
32687                 },
32688                 {
32689                     cls : 'tooltip-inner'
32690                 }
32691            ]
32692         };
32693         
32694         return cfg;
32695     },
32696     bind : function(el)
32697     {
32698         this.bindEl = el;
32699     },
32700     
32701     initEvents : function()
32702     {
32703         this.arrowEl = this.el.select('.arrow', true).first();
32704         this.innerEl = this.el.select('.tooltip-inner', true).first();
32705     },
32706     
32707     enter : function () {
32708        
32709         if (this.timeout != null) {
32710             clearTimeout(this.timeout);
32711         }
32712         
32713         this.hoverState = 'in';
32714          //Roo.log("enter - show");
32715         if (!this.delay || !this.delay.show) {
32716             this.show();
32717             return;
32718         }
32719         var _t = this;
32720         this.timeout = setTimeout(function () {
32721             if (_t.hoverState == 'in') {
32722                 _t.show();
32723             }
32724         }, this.delay.show);
32725     },
32726     leave : function()
32727     {
32728         clearTimeout(this.timeout);
32729     
32730         this.hoverState = 'out';
32731          if (!this.delay || !this.delay.hide) {
32732             this.hide();
32733             return;
32734         }
32735        
32736         var _t = this;
32737         this.timeout = setTimeout(function () {
32738             //Roo.log("leave - timeout");
32739             
32740             if (_t.hoverState == 'out') {
32741                 _t.hide();
32742                 Roo.bootstrap.Tooltip.currentEl = false;
32743             }
32744         }, delay);
32745     },
32746     
32747     show : function (msg)
32748     {
32749         if (!this.el) {
32750             this.render(document.body);
32751         }
32752         // set content.
32753         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32754         
32755         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32756         
32757         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32758         
32759         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32760                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32761
32762         if(this.bindEl.attr('tooltip-class')) {
32763             this.el.addClass(this.bindEl.attr('tooltip-class'));
32764         }
32765         
32766         var placement = typeof this.placement == 'function' ?
32767             this.placement.call(this, this.el, on_el) :
32768             this.placement;
32769         
32770         if(this.bindEl.attr('tooltip-placement')) {
32771             placement = this.bindEl.attr('tooltip-placement');
32772         }
32773             
32774         var autoToken = /\s?auto?\s?/i;
32775         var autoPlace = autoToken.test(placement);
32776         if (autoPlace) {
32777             placement = placement.replace(autoToken, '') || 'top';
32778         }
32779         
32780         //this.el.detach()
32781         //this.el.setXY([0,0]);
32782         this.el.show();
32783         //this.el.dom.style.display='block';
32784         
32785         //this.el.appendTo(on_el);
32786         
32787         var p = this.getPosition();
32788         var box = this.el.getBox();
32789         
32790         if (autoPlace) {
32791             // fixme..
32792         }
32793         
32794         var align = this.alignment[placement];
32795         
32796         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32797         
32798         if(placement == 'top' || placement == 'bottom'){
32799             if(xy[0] < 0){
32800                 placement = 'right';
32801             }
32802             
32803             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32804                 placement = 'left';
32805             }
32806             
32807             var scroll = Roo.select('body', true).first().getScroll();
32808             
32809             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32810                 placement = 'top';
32811             }
32812             
32813             align = this.alignment[placement];
32814             
32815             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32816             
32817         }
32818         
32819         var elems = document.getElementsByTagName('div');
32820         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32821         for (var i = 0; i < elems.length; i++) {
32822           var zindex = Number.parseInt(
32823                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32824                 10
32825           );
32826           if (zindex > highest) {
32827             highest = zindex;
32828           }
32829         }
32830         
32831         
32832         
32833         this.el.dom.style.zIndex = highest;
32834         
32835         this.el.alignTo(this.bindEl, align[0],align[1]);
32836         //var arrow = this.el.select('.arrow',true).first();
32837         //arrow.set(align[2], 
32838         
32839         this.el.addClass(placement);
32840         this.el.addClass("bs-tooltip-"+ placement);
32841         
32842         this.el.addClass('in fade show');
32843         
32844         this.hoverState = null;
32845         
32846         if (this.el.hasClass('fade')) {
32847             // fade it?
32848         }
32849         
32850         
32851         
32852         
32853         
32854     },
32855     hide : function()
32856     {
32857          
32858         if (!this.el) {
32859             return;
32860         }
32861         //this.el.setXY([0,0]);
32862         if(this.bindEl.attr('tooltip-class')) {
32863             this.el.removeClass(this.bindEl.attr('tooltip-class'));
32864         }
32865         this.el.removeClass(['show', 'in']);
32866         //this.el.hide();
32867         
32868     }
32869     
32870 });
32871  
32872
32873  /*
32874  * - LGPL
32875  *
32876  * Location Picker
32877  * 
32878  */
32879
32880 /**
32881  * @class Roo.bootstrap.LocationPicker
32882  * @extends Roo.bootstrap.Component
32883  * Bootstrap LocationPicker class
32884  * @cfg {Number} latitude Position when init default 0
32885  * @cfg {Number} longitude Position when init default 0
32886  * @cfg {Number} zoom default 15
32887  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32888  * @cfg {Boolean} mapTypeControl default false
32889  * @cfg {Boolean} disableDoubleClickZoom default false
32890  * @cfg {Boolean} scrollwheel default true
32891  * @cfg {Boolean} streetViewControl default false
32892  * @cfg {Number} radius default 0
32893  * @cfg {String} locationName
32894  * @cfg {Boolean} draggable default true
32895  * @cfg {Boolean} enableAutocomplete default false
32896  * @cfg {Boolean} enableReverseGeocode default true
32897  * @cfg {String} markerTitle
32898  * 
32899  * @constructor
32900  * Create a new LocationPicker
32901  * @param {Object} config The config object
32902  */
32903
32904
32905 Roo.bootstrap.LocationPicker = function(config){
32906     
32907     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32908     
32909     this.addEvents({
32910         /**
32911          * @event initial
32912          * Fires when the picker initialized.
32913          * @param {Roo.bootstrap.LocationPicker} this
32914          * @param {Google Location} location
32915          */
32916         initial : true,
32917         /**
32918          * @event positionchanged
32919          * Fires when the picker position changed.
32920          * @param {Roo.bootstrap.LocationPicker} this
32921          * @param {Google Location} location
32922          */
32923         positionchanged : true,
32924         /**
32925          * @event resize
32926          * Fires when the map resize.
32927          * @param {Roo.bootstrap.LocationPicker} this
32928          */
32929         resize : true,
32930         /**
32931          * @event show
32932          * Fires when the map show.
32933          * @param {Roo.bootstrap.LocationPicker} this
32934          */
32935         show : true,
32936         /**
32937          * @event hide
32938          * Fires when the map hide.
32939          * @param {Roo.bootstrap.LocationPicker} this
32940          */
32941         hide : true,
32942         /**
32943          * @event mapClick
32944          * Fires when click the map.
32945          * @param {Roo.bootstrap.LocationPicker} this
32946          * @param {Map event} e
32947          */
32948         mapClick : true,
32949         /**
32950          * @event mapRightClick
32951          * Fires when right click the map.
32952          * @param {Roo.bootstrap.LocationPicker} this
32953          * @param {Map event} e
32954          */
32955         mapRightClick : true,
32956         /**
32957          * @event markerClick
32958          * Fires when click the marker.
32959          * @param {Roo.bootstrap.LocationPicker} this
32960          * @param {Map event} e
32961          */
32962         markerClick : true,
32963         /**
32964          * @event markerRightClick
32965          * Fires when right click the marker.
32966          * @param {Roo.bootstrap.LocationPicker} this
32967          * @param {Map event} e
32968          */
32969         markerRightClick : true,
32970         /**
32971          * @event OverlayViewDraw
32972          * Fires when OverlayView Draw
32973          * @param {Roo.bootstrap.LocationPicker} this
32974          */
32975         OverlayViewDraw : true,
32976         /**
32977          * @event OverlayViewOnAdd
32978          * Fires when OverlayView Draw
32979          * @param {Roo.bootstrap.LocationPicker} this
32980          */
32981         OverlayViewOnAdd : true,
32982         /**
32983          * @event OverlayViewOnRemove
32984          * Fires when OverlayView Draw
32985          * @param {Roo.bootstrap.LocationPicker} this
32986          */
32987         OverlayViewOnRemove : true,
32988         /**
32989          * @event OverlayViewShow
32990          * Fires when OverlayView Draw
32991          * @param {Roo.bootstrap.LocationPicker} this
32992          * @param {Pixel} cpx
32993          */
32994         OverlayViewShow : true,
32995         /**
32996          * @event OverlayViewHide
32997          * Fires when OverlayView Draw
32998          * @param {Roo.bootstrap.LocationPicker} this
32999          */
33000         OverlayViewHide : true,
33001         /**
33002          * @event loadexception
33003          * Fires when load google lib failed.
33004          * @param {Roo.bootstrap.LocationPicker} this
33005          */
33006         loadexception : true
33007     });
33008         
33009 };
33010
33011 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33012     
33013     gMapContext: false,
33014     
33015     latitude: 0,
33016     longitude: 0,
33017     zoom: 15,
33018     mapTypeId: false,
33019     mapTypeControl: false,
33020     disableDoubleClickZoom: false,
33021     scrollwheel: true,
33022     streetViewControl: false,
33023     radius: 0,
33024     locationName: '',
33025     draggable: true,
33026     enableAutocomplete: false,
33027     enableReverseGeocode: true,
33028     markerTitle: '',
33029     
33030     getAutoCreate: function()
33031     {
33032
33033         var cfg = {
33034             tag: 'div',
33035             cls: 'roo-location-picker'
33036         };
33037         
33038         return cfg
33039     },
33040     
33041     initEvents: function(ct, position)
33042     {       
33043         if(!this.el.getWidth() || this.isApplied()){
33044             return;
33045         }
33046         
33047         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33048         
33049         this.initial();
33050     },
33051     
33052     initial: function()
33053     {
33054         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33055             this.fireEvent('loadexception', this);
33056             return;
33057         }
33058         
33059         if(!this.mapTypeId){
33060             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33061         }
33062         
33063         this.gMapContext = this.GMapContext();
33064         
33065         this.initOverlayView();
33066         
33067         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33068         
33069         var _this = this;
33070                 
33071         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33072             _this.setPosition(_this.gMapContext.marker.position);
33073         });
33074         
33075         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33076             _this.fireEvent('mapClick', this, event);
33077             
33078         });
33079
33080         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33081             _this.fireEvent('mapRightClick', this, event);
33082             
33083         });
33084         
33085         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33086             _this.fireEvent('markerClick', this, event);
33087             
33088         });
33089
33090         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33091             _this.fireEvent('markerRightClick', this, event);
33092             
33093         });
33094         
33095         this.setPosition(this.gMapContext.location);
33096         
33097         this.fireEvent('initial', this, this.gMapContext.location);
33098     },
33099     
33100     initOverlayView: function()
33101     {
33102         var _this = this;
33103         
33104         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33105             
33106             draw: function()
33107             {
33108                 _this.fireEvent('OverlayViewDraw', _this);
33109             },
33110             
33111             onAdd: function()
33112             {
33113                 _this.fireEvent('OverlayViewOnAdd', _this);
33114             },
33115             
33116             onRemove: function()
33117             {
33118                 _this.fireEvent('OverlayViewOnRemove', _this);
33119             },
33120             
33121             show: function(cpx)
33122             {
33123                 _this.fireEvent('OverlayViewShow', _this, cpx);
33124             },
33125             
33126             hide: function()
33127             {
33128                 _this.fireEvent('OverlayViewHide', _this);
33129             }
33130             
33131         });
33132     },
33133     
33134     fromLatLngToContainerPixel: function(event)
33135     {
33136         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33137     },
33138     
33139     isApplied: function() 
33140     {
33141         return this.getGmapContext() == false ? false : true;
33142     },
33143     
33144     getGmapContext: function() 
33145     {
33146         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33147     },
33148     
33149     GMapContext: function() 
33150     {
33151         var position = new google.maps.LatLng(this.latitude, this.longitude);
33152         
33153         var _map = new google.maps.Map(this.el.dom, {
33154             center: position,
33155             zoom: this.zoom,
33156             mapTypeId: this.mapTypeId,
33157             mapTypeControl: this.mapTypeControl,
33158             disableDoubleClickZoom: this.disableDoubleClickZoom,
33159             scrollwheel: this.scrollwheel,
33160             streetViewControl: this.streetViewControl,
33161             locationName: this.locationName,
33162             draggable: this.draggable,
33163             enableAutocomplete: this.enableAutocomplete,
33164             enableReverseGeocode: this.enableReverseGeocode
33165         });
33166         
33167         var _marker = new google.maps.Marker({
33168             position: position,
33169             map: _map,
33170             title: this.markerTitle,
33171             draggable: this.draggable
33172         });
33173         
33174         return {
33175             map: _map,
33176             marker: _marker,
33177             circle: null,
33178             location: position,
33179             radius: this.radius,
33180             locationName: this.locationName,
33181             addressComponents: {
33182                 formatted_address: null,
33183                 addressLine1: null,
33184                 addressLine2: null,
33185                 streetName: null,
33186                 streetNumber: null,
33187                 city: null,
33188                 district: null,
33189                 state: null,
33190                 stateOrProvince: null
33191             },
33192             settings: this,
33193             domContainer: this.el.dom,
33194             geodecoder: new google.maps.Geocoder()
33195         };
33196     },
33197     
33198     drawCircle: function(center, radius, options) 
33199     {
33200         if (this.gMapContext.circle != null) {
33201             this.gMapContext.circle.setMap(null);
33202         }
33203         if (radius > 0) {
33204             radius *= 1;
33205             options = Roo.apply({}, options, {
33206                 strokeColor: "#0000FF",
33207                 strokeOpacity: .35,
33208                 strokeWeight: 2,
33209                 fillColor: "#0000FF",
33210                 fillOpacity: .2
33211             });
33212             
33213             options.map = this.gMapContext.map;
33214             options.radius = radius;
33215             options.center = center;
33216             this.gMapContext.circle = new google.maps.Circle(options);
33217             return this.gMapContext.circle;
33218         }
33219         
33220         return null;
33221     },
33222     
33223     setPosition: function(location) 
33224     {
33225         this.gMapContext.location = location;
33226         this.gMapContext.marker.setPosition(location);
33227         this.gMapContext.map.panTo(location);
33228         this.drawCircle(location, this.gMapContext.radius, {});
33229         
33230         var _this = this;
33231         
33232         if (this.gMapContext.settings.enableReverseGeocode) {
33233             this.gMapContext.geodecoder.geocode({
33234                 latLng: this.gMapContext.location
33235             }, function(results, status) {
33236                 
33237                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33238                     _this.gMapContext.locationName = results[0].formatted_address;
33239                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33240                     
33241                     _this.fireEvent('positionchanged', this, location);
33242                 }
33243             });
33244             
33245             return;
33246         }
33247         
33248         this.fireEvent('positionchanged', this, location);
33249     },
33250     
33251     resize: function()
33252     {
33253         google.maps.event.trigger(this.gMapContext.map, "resize");
33254         
33255         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33256         
33257         this.fireEvent('resize', this);
33258     },
33259     
33260     setPositionByLatLng: function(latitude, longitude)
33261     {
33262         this.setPosition(new google.maps.LatLng(latitude, longitude));
33263     },
33264     
33265     getCurrentPosition: function() 
33266     {
33267         return {
33268             latitude: this.gMapContext.location.lat(),
33269             longitude: this.gMapContext.location.lng()
33270         };
33271     },
33272     
33273     getAddressName: function() 
33274     {
33275         return this.gMapContext.locationName;
33276     },
33277     
33278     getAddressComponents: function() 
33279     {
33280         return this.gMapContext.addressComponents;
33281     },
33282     
33283     address_component_from_google_geocode: function(address_components) 
33284     {
33285         var result = {};
33286         
33287         for (var i = 0; i < address_components.length; i++) {
33288             var component = address_components[i];
33289             if (component.types.indexOf("postal_code") >= 0) {
33290                 result.postalCode = component.short_name;
33291             } else if (component.types.indexOf("street_number") >= 0) {
33292                 result.streetNumber = component.short_name;
33293             } else if (component.types.indexOf("route") >= 0) {
33294                 result.streetName = component.short_name;
33295             } else if (component.types.indexOf("neighborhood") >= 0) {
33296                 result.city = component.short_name;
33297             } else if (component.types.indexOf("locality") >= 0) {
33298                 result.city = component.short_name;
33299             } else if (component.types.indexOf("sublocality") >= 0) {
33300                 result.district = component.short_name;
33301             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33302                 result.stateOrProvince = component.short_name;
33303             } else if (component.types.indexOf("country") >= 0) {
33304                 result.country = component.short_name;
33305             }
33306         }
33307         
33308         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33309         result.addressLine2 = "";
33310         return result;
33311     },
33312     
33313     setZoomLevel: function(zoom)
33314     {
33315         this.gMapContext.map.setZoom(zoom);
33316     },
33317     
33318     show: function()
33319     {
33320         if(!this.el){
33321             return;
33322         }
33323         
33324         this.el.show();
33325         
33326         this.resize();
33327         
33328         this.fireEvent('show', this);
33329     },
33330     
33331     hide: function()
33332     {
33333         if(!this.el){
33334             return;
33335         }
33336         
33337         this.el.hide();
33338         
33339         this.fireEvent('hide', this);
33340     }
33341     
33342 });
33343
33344 Roo.apply(Roo.bootstrap.LocationPicker, {
33345     
33346     OverlayView : function(map, options)
33347     {
33348         options = options || {};
33349         
33350         this.setMap(map);
33351     }
33352     
33353     
33354 });/**
33355  * @class Roo.bootstrap.Alert
33356  * @extends Roo.bootstrap.Component
33357  * Bootstrap Alert class - shows an alert area box
33358  * eg
33359  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33360   Enter a valid email address
33361 </div>
33362  * @licence LGPL
33363  * @cfg {String} title The title of alert
33364  * @cfg {String} html The content of alert
33365  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33366  * @cfg {String} fa font-awesomeicon
33367  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33368  * @cfg {Boolean} close true to show a x closer
33369  * 
33370  * 
33371  * @constructor
33372  * Create a new alert
33373  * @param {Object} config The config object
33374  */
33375
33376
33377 Roo.bootstrap.Alert = function(config){
33378     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33379     
33380 };
33381
33382 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33383     
33384     title: '',
33385     html: '',
33386     weight: false,
33387     fa: false,
33388     faicon: false, // BC
33389     close : false,
33390     
33391     
33392     getAutoCreate : function()
33393     {
33394         
33395         var cfg = {
33396             tag : 'div',
33397             cls : 'alert',
33398             cn : [
33399                 {
33400                     tag: 'button',
33401                     type :  "button",
33402                     cls: "close",
33403                     html : '×',
33404                     style : this.close ? '' : 'display:none'
33405                 },
33406                 {
33407                     tag : 'i',
33408                     cls : 'roo-alert-icon'
33409                     
33410                 },
33411                 {
33412                     tag : 'b',
33413                     cls : 'roo-alert-title',
33414                     html : this.title
33415                 },
33416                 {
33417                     tag : 'span',
33418                     cls : 'roo-alert-text',
33419                     html : this.html
33420                 }
33421             ]
33422         };
33423         
33424         if(this.faicon){
33425             cfg.cn[0].cls += ' fa ' + this.faicon;
33426         }
33427         if(this.fa){
33428             cfg.cn[0].cls += ' fa ' + this.fa;
33429         }
33430         
33431         if(this.weight){
33432             cfg.cls += ' alert-' + this.weight;
33433         }
33434         
33435         return cfg;
33436     },
33437     
33438     initEvents: function() 
33439     {
33440         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33441         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33442         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33443         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33444         if (this.seconds > 0) {
33445             this.hide.defer(this.seconds, this);
33446         }
33447     },
33448     /**
33449      * Set the Title Message HTML
33450      * @param {String} html
33451      */
33452     setTitle : function(str)
33453     {
33454         this.titleEl.dom.innerHTML = str;
33455     },
33456      
33457      /**
33458      * Set the Body Message HTML
33459      * @param {String} html
33460      */
33461     setHtml : function(str)
33462     {
33463         this.htmlEl.dom.innerHTML = str;
33464     },
33465     /**
33466      * Set the Weight of the alert
33467      * @param {String} (success|info|warning|danger) weight
33468      */
33469     
33470     setWeight : function(weight)
33471     {
33472         if(this.weight){
33473             this.el.removeClass('alert-' + this.weight);
33474         }
33475         
33476         this.weight = weight;
33477         
33478         this.el.addClass('alert-' + this.weight);
33479     },
33480       /**
33481      * Set the Icon of the alert
33482      * @param {String} see fontawsome names (name without the 'fa-' bit)
33483      */
33484     setIcon : function(icon)
33485     {
33486         if(this.faicon){
33487             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33488         }
33489         
33490         this.faicon = icon;
33491         
33492         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33493     },
33494     /**
33495      * Hide the Alert
33496      */
33497     hide: function() 
33498     {
33499         this.el.hide();   
33500     },
33501     /**
33502      * Show the Alert
33503      */
33504     show: function() 
33505     {  
33506         this.el.show();   
33507     }
33508     
33509 });
33510
33511  
33512 /*
33513 * Licence: LGPL
33514 */
33515
33516 /**
33517  * @class Roo.bootstrap.UploadCropbox
33518  * @extends Roo.bootstrap.Component
33519  * Bootstrap UploadCropbox class
33520  * @cfg {String} emptyText show when image has been loaded
33521  * @cfg {String} rotateNotify show when image too small to rotate
33522  * @cfg {Number} errorTimeout default 3000
33523  * @cfg {Number} minWidth default 300
33524  * @cfg {Number} minHeight default 300
33525  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33526  * @cfg {Boolean} isDocument (true|false) default false
33527  * @cfg {String} url action url
33528  * @cfg {String} paramName default 'imageUpload'
33529  * @cfg {String} method default POST
33530  * @cfg {Boolean} loadMask (true|false) default true
33531  * @cfg {Boolean} loadingText default 'Loading...'
33532  * 
33533  * @constructor
33534  * Create a new UploadCropbox
33535  * @param {Object} config The config object
33536  */
33537
33538 Roo.bootstrap.UploadCropbox = function(config){
33539     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33540     
33541     this.addEvents({
33542         /**
33543          * @event beforeselectfile
33544          * Fire before select file
33545          * @param {Roo.bootstrap.UploadCropbox} this
33546          */
33547         "beforeselectfile" : true,
33548         /**
33549          * @event initial
33550          * Fire after initEvent
33551          * @param {Roo.bootstrap.UploadCropbox} this
33552          */
33553         "initial" : true,
33554         /**
33555          * @event crop
33556          * Fire after initEvent
33557          * @param {Roo.bootstrap.UploadCropbox} this
33558          * @param {String} data
33559          */
33560         "crop" : true,
33561         /**
33562          * @event prepare
33563          * Fire when preparing the file data
33564          * @param {Roo.bootstrap.UploadCropbox} this
33565          * @param {Object} file
33566          */
33567         "prepare" : true,
33568         /**
33569          * @event exception
33570          * Fire when get exception
33571          * @param {Roo.bootstrap.UploadCropbox} this
33572          * @param {XMLHttpRequest} xhr
33573          */
33574         "exception" : true,
33575         /**
33576          * @event beforeloadcanvas
33577          * Fire before load the canvas
33578          * @param {Roo.bootstrap.UploadCropbox} this
33579          * @param {String} src
33580          */
33581         "beforeloadcanvas" : true,
33582         /**
33583          * @event trash
33584          * Fire when trash image
33585          * @param {Roo.bootstrap.UploadCropbox} this
33586          */
33587         "trash" : true,
33588         /**
33589          * @event download
33590          * Fire when download the image
33591          * @param {Roo.bootstrap.UploadCropbox} this
33592          */
33593         "download" : true,
33594         /**
33595          * @event footerbuttonclick
33596          * Fire when footerbuttonclick
33597          * @param {Roo.bootstrap.UploadCropbox} this
33598          * @param {String} type
33599          */
33600         "footerbuttonclick" : true,
33601         /**
33602          * @event resize
33603          * Fire when resize
33604          * @param {Roo.bootstrap.UploadCropbox} this
33605          */
33606         "resize" : true,
33607         /**
33608          * @event rotate
33609          * Fire when rotate the image
33610          * @param {Roo.bootstrap.UploadCropbox} this
33611          * @param {String} pos
33612          */
33613         "rotate" : true,
33614         /**
33615          * @event inspect
33616          * Fire when inspect the file
33617          * @param {Roo.bootstrap.UploadCropbox} this
33618          * @param {Object} file
33619          */
33620         "inspect" : true,
33621         /**
33622          * @event upload
33623          * Fire when xhr upload the file
33624          * @param {Roo.bootstrap.UploadCropbox} this
33625          * @param {Object} data
33626          */
33627         "upload" : true,
33628         /**
33629          * @event arrange
33630          * Fire when arrange the file data
33631          * @param {Roo.bootstrap.UploadCropbox} this
33632          * @param {Object} formData
33633          */
33634         "arrange" : true
33635     });
33636     
33637     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33638 };
33639
33640 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33641     
33642     emptyText : 'Click to upload image',
33643     rotateNotify : 'Image is too small to rotate',
33644     errorTimeout : 3000,
33645     scale : 0,
33646     baseScale : 1,
33647     rotate : 0,
33648     dragable : false,
33649     pinching : false,
33650     mouseX : 0,
33651     mouseY : 0,
33652     cropData : false,
33653     minWidth : 300,
33654     minHeight : 300,
33655     file : false,
33656     exif : {},
33657     baseRotate : 1,
33658     cropType : 'image/jpeg',
33659     buttons : false,
33660     canvasLoaded : false,
33661     isDocument : false,
33662     method : 'POST',
33663     paramName : 'imageUpload',
33664     loadMask : true,
33665     loadingText : 'Loading...',
33666     maskEl : false,
33667     
33668     getAutoCreate : function()
33669     {
33670         var cfg = {
33671             tag : 'div',
33672             cls : 'roo-upload-cropbox',
33673             cn : [
33674                 {
33675                     tag : 'input',
33676                     cls : 'roo-upload-cropbox-selector',
33677                     type : 'file'
33678                 },
33679                 {
33680                     tag : 'div',
33681                     cls : 'roo-upload-cropbox-body',
33682                     style : 'cursor:pointer',
33683                     cn : [
33684                         {
33685                             tag : 'div',
33686                             cls : 'roo-upload-cropbox-preview'
33687                         },
33688                         {
33689                             tag : 'div',
33690                             cls : 'roo-upload-cropbox-thumb'
33691                         },
33692                         {
33693                             tag : 'div',
33694                             cls : 'roo-upload-cropbox-empty-notify',
33695                             html : this.emptyText
33696                         },
33697                         {
33698                             tag : 'div',
33699                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33700                             html : this.rotateNotify
33701                         }
33702                     ]
33703                 },
33704                 {
33705                     tag : 'div',
33706                     cls : 'roo-upload-cropbox-footer',
33707                     cn : {
33708                         tag : 'div',
33709                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33710                         cn : []
33711                     }
33712                 }
33713             ]
33714         };
33715         
33716         return cfg;
33717     },
33718     
33719     onRender : function(ct, position)
33720     {
33721         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33722         
33723         if (this.buttons.length) {
33724             
33725             Roo.each(this.buttons, function(bb) {
33726                 
33727                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33728                 
33729                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33730                 
33731             }, this);
33732         }
33733         
33734         if(this.loadMask){
33735             this.maskEl = this.el;
33736         }
33737     },
33738     
33739     initEvents : function()
33740     {
33741         this.urlAPI = (window.createObjectURL && window) || 
33742                                 (window.URL && URL.revokeObjectURL && URL) || 
33743                                 (window.webkitURL && webkitURL);
33744                         
33745         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33746         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33747         
33748         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33749         this.selectorEl.hide();
33750         
33751         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33752         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33753         
33754         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33755         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33756         this.thumbEl.hide();
33757         
33758         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33759         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33760         
33761         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33762         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33763         this.errorEl.hide();
33764         
33765         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33766         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33767         this.footerEl.hide();
33768         
33769         this.setThumbBoxSize();
33770         
33771         this.bind();
33772         
33773         this.resize();
33774         
33775         this.fireEvent('initial', this);
33776     },
33777
33778     bind : function()
33779     {
33780         var _this = this;
33781         
33782         window.addEventListener("resize", function() { _this.resize(); } );
33783         
33784         this.bodyEl.on('click', this.beforeSelectFile, this);
33785         
33786         if(Roo.isTouch){
33787             this.bodyEl.on('touchstart', this.onTouchStart, this);
33788             this.bodyEl.on('touchmove', this.onTouchMove, this);
33789             this.bodyEl.on('touchend', this.onTouchEnd, this);
33790         }
33791         
33792         if(!Roo.isTouch){
33793             this.bodyEl.on('mousedown', this.onMouseDown, this);
33794             this.bodyEl.on('mousemove', this.onMouseMove, this);
33795             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33796             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33797             Roo.get(document).on('mouseup', this.onMouseUp, this);
33798         }
33799         
33800         this.selectorEl.on('change', this.onFileSelected, this);
33801     },
33802     
33803     reset : function()
33804     {    
33805         this.scale = 0;
33806         this.baseScale = 1;
33807         this.rotate = 0;
33808         this.baseRotate = 1;
33809         this.dragable = false;
33810         this.pinching = false;
33811         this.mouseX = 0;
33812         this.mouseY = 0;
33813         this.cropData = false;
33814         this.notifyEl.dom.innerHTML = this.emptyText;
33815         
33816         this.selectorEl.dom.value = '';
33817         
33818     },
33819     
33820     resize : function()
33821     {
33822         if(this.fireEvent('resize', this) != false){
33823             this.setThumbBoxPosition();
33824             this.setCanvasPosition();
33825         }
33826     },
33827     
33828     onFooterButtonClick : function(e, el, o, type)
33829     {
33830         switch (type) {
33831             case 'rotate-left' :
33832                 this.onRotateLeft(e);
33833                 break;
33834             case 'rotate-right' :
33835                 this.onRotateRight(e);
33836                 break;
33837             case 'picture' :
33838                 this.beforeSelectFile(e);
33839                 break;
33840             case 'trash' :
33841                 this.trash(e);
33842                 break;
33843             case 'crop' :
33844                 this.crop(e);
33845                 break;
33846             case 'download' :
33847                 this.download(e);
33848                 break;
33849             default :
33850                 break;
33851         }
33852         
33853         this.fireEvent('footerbuttonclick', this, type);
33854     },
33855     
33856     beforeSelectFile : function(e)
33857     {
33858         e.preventDefault();
33859         
33860         if(this.fireEvent('beforeselectfile', this) != false){
33861             this.selectorEl.dom.click();
33862         }
33863     },
33864     
33865     onFileSelected : function(e)
33866     {
33867         e.preventDefault();
33868         
33869         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33870             return;
33871         }
33872         
33873         var file = this.selectorEl.dom.files[0];
33874         
33875         if(this.fireEvent('inspect', this, file) != false){
33876             this.prepare(file);
33877         }
33878         
33879     },
33880     
33881     trash : function(e)
33882     {
33883         this.fireEvent('trash', this);
33884     },
33885     
33886     download : function(e)
33887     {
33888         this.fireEvent('download', this);
33889     },
33890     
33891     loadCanvas : function(src)
33892     {   
33893         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33894             
33895             this.reset();
33896             
33897             this.imageEl = document.createElement('img');
33898             
33899             var _this = this;
33900             
33901             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33902             
33903             this.imageEl.src = src;
33904         }
33905     },
33906     
33907     onLoadCanvas : function()
33908     {   
33909         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33910         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33911         
33912         this.bodyEl.un('click', this.beforeSelectFile, this);
33913         
33914         this.notifyEl.hide();
33915         this.thumbEl.show();
33916         this.footerEl.show();
33917         
33918         this.baseRotateLevel();
33919         
33920         if(this.isDocument){
33921             this.setThumbBoxSize();
33922         }
33923         
33924         this.setThumbBoxPosition();
33925         
33926         this.baseScaleLevel();
33927         
33928         this.draw();
33929         
33930         this.resize();
33931         
33932         this.canvasLoaded = true;
33933         
33934         if(this.loadMask){
33935             this.maskEl.unmask();
33936         }
33937         
33938     },
33939     
33940     setCanvasPosition : function()
33941     {   
33942         if(!this.canvasEl){
33943             return;
33944         }
33945         
33946         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33947         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33948         
33949         this.previewEl.setLeft(pw);
33950         this.previewEl.setTop(ph);
33951         
33952     },
33953     
33954     onMouseDown : function(e)
33955     {   
33956         e.stopEvent();
33957         
33958         this.dragable = true;
33959         this.pinching = false;
33960         
33961         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33962             this.dragable = false;
33963             return;
33964         }
33965         
33966         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33967         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33968         
33969     },
33970     
33971     onMouseMove : function(e)
33972     {   
33973         e.stopEvent();
33974         
33975         if(!this.canvasLoaded){
33976             return;
33977         }
33978         
33979         if (!this.dragable){
33980             return;
33981         }
33982         
33983         var minX = Math.ceil(this.thumbEl.getLeft(true));
33984         var minY = Math.ceil(this.thumbEl.getTop(true));
33985         
33986         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33987         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33988         
33989         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33990         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33991         
33992         x = x - this.mouseX;
33993         y = y - this.mouseY;
33994         
33995         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33996         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33997         
33998         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33999         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
34000         
34001         this.previewEl.setLeft(bgX);
34002         this.previewEl.setTop(bgY);
34003         
34004         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34005         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34006     },
34007     
34008     onMouseUp : function(e)
34009     {   
34010         e.stopEvent();
34011         
34012         this.dragable = false;
34013     },
34014     
34015     onMouseWheel : function(e)
34016     {   
34017         e.stopEvent();
34018         
34019         this.startScale = this.scale;
34020         
34021         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34022         
34023         if(!this.zoomable()){
34024             this.scale = this.startScale;
34025             return;
34026         }
34027         
34028         this.draw();
34029         
34030         return;
34031     },
34032     
34033     zoomable : function()
34034     {
34035         var minScale = this.thumbEl.getWidth() / this.minWidth;
34036         
34037         if(this.minWidth < this.minHeight){
34038             minScale = this.thumbEl.getHeight() / this.minHeight;
34039         }
34040         
34041         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34042         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34043         
34044         if(
34045                 this.isDocument &&
34046                 (this.rotate == 0 || this.rotate == 180) && 
34047                 (
34048                     width > this.imageEl.OriginWidth || 
34049                     height > this.imageEl.OriginHeight ||
34050                     (width < this.minWidth && height < this.minHeight)
34051                 )
34052         ){
34053             return false;
34054         }
34055         
34056         if(
34057                 this.isDocument &&
34058                 (this.rotate == 90 || this.rotate == 270) && 
34059                 (
34060                     width > this.imageEl.OriginWidth || 
34061                     height > this.imageEl.OriginHeight ||
34062                     (width < this.minHeight && height < this.minWidth)
34063                 )
34064         ){
34065             return false;
34066         }
34067         
34068         if(
34069                 !this.isDocument &&
34070                 (this.rotate == 0 || this.rotate == 180) && 
34071                 (
34072                     width < this.minWidth || 
34073                     width > this.imageEl.OriginWidth || 
34074                     height < this.minHeight || 
34075                     height > this.imageEl.OriginHeight
34076                 )
34077         ){
34078             return false;
34079         }
34080         
34081         if(
34082                 !this.isDocument &&
34083                 (this.rotate == 90 || this.rotate == 270) && 
34084                 (
34085                     width < this.minHeight || 
34086                     width > this.imageEl.OriginWidth || 
34087                     height < this.minWidth || 
34088                     height > this.imageEl.OriginHeight
34089                 )
34090         ){
34091             return false;
34092         }
34093         
34094         return true;
34095         
34096     },
34097     
34098     onRotateLeft : function(e)
34099     {   
34100         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34101             
34102             var minScale = this.thumbEl.getWidth() / this.minWidth;
34103             
34104             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34105             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34106             
34107             this.startScale = this.scale;
34108             
34109             while (this.getScaleLevel() < minScale){
34110             
34111                 this.scale = this.scale + 1;
34112                 
34113                 if(!this.zoomable()){
34114                     break;
34115                 }
34116                 
34117                 if(
34118                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34119                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34120                 ){
34121                     continue;
34122                 }
34123                 
34124                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34125
34126                 this.draw();
34127                 
34128                 return;
34129             }
34130             
34131             this.scale = this.startScale;
34132             
34133             this.onRotateFail();
34134             
34135             return false;
34136         }
34137         
34138         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34139
34140         if(this.isDocument){
34141             this.setThumbBoxSize();
34142             this.setThumbBoxPosition();
34143             this.setCanvasPosition();
34144         }
34145         
34146         this.draw();
34147         
34148         this.fireEvent('rotate', this, 'left');
34149         
34150     },
34151     
34152     onRotateRight : function(e)
34153     {
34154         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34155             
34156             var minScale = this.thumbEl.getWidth() / this.minWidth;
34157         
34158             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34159             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34160             
34161             this.startScale = this.scale;
34162             
34163             while (this.getScaleLevel() < minScale){
34164             
34165                 this.scale = this.scale + 1;
34166                 
34167                 if(!this.zoomable()){
34168                     break;
34169                 }
34170                 
34171                 if(
34172                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34173                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34174                 ){
34175                     continue;
34176                 }
34177                 
34178                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34179
34180                 this.draw();
34181                 
34182                 return;
34183             }
34184             
34185             this.scale = this.startScale;
34186             
34187             this.onRotateFail();
34188             
34189             return false;
34190         }
34191         
34192         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34193
34194         if(this.isDocument){
34195             this.setThumbBoxSize();
34196             this.setThumbBoxPosition();
34197             this.setCanvasPosition();
34198         }
34199         
34200         this.draw();
34201         
34202         this.fireEvent('rotate', this, 'right');
34203     },
34204     
34205     onRotateFail : function()
34206     {
34207         this.errorEl.show(true);
34208         
34209         var _this = this;
34210         
34211         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34212     },
34213     
34214     draw : function()
34215     {
34216         this.previewEl.dom.innerHTML = '';
34217         
34218         var canvasEl = document.createElement("canvas");
34219         
34220         var contextEl = canvasEl.getContext("2d");
34221         
34222         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34223         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34224         var center = this.imageEl.OriginWidth / 2;
34225         
34226         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34227             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34228             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34229             center = this.imageEl.OriginHeight / 2;
34230         }
34231         
34232         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34233         
34234         contextEl.translate(center, center);
34235         contextEl.rotate(this.rotate * Math.PI / 180);
34236
34237         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34238         
34239         this.canvasEl = document.createElement("canvas");
34240         
34241         this.contextEl = this.canvasEl.getContext("2d");
34242         
34243         switch (this.rotate) {
34244             case 0 :
34245                 
34246                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34247                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34248                 
34249                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34250                 
34251                 break;
34252             case 90 : 
34253                 
34254                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34255                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34256                 
34257                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34258                     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);
34259                     break;
34260                 }
34261                 
34262                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34263                 
34264                 break;
34265             case 180 :
34266                 
34267                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34268                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34269                 
34270                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34271                     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);
34272                     break;
34273                 }
34274                 
34275                 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);
34276                 
34277                 break;
34278             case 270 :
34279                 
34280                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34281                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34282         
34283                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34284                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34285                     break;
34286                 }
34287                 
34288                 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);
34289                 
34290                 break;
34291             default : 
34292                 break;
34293         }
34294         
34295         this.previewEl.appendChild(this.canvasEl);
34296         
34297         this.setCanvasPosition();
34298     },
34299     
34300     crop : function()
34301     {
34302         if(!this.canvasLoaded){
34303             return;
34304         }
34305         
34306         var imageCanvas = document.createElement("canvas");
34307         
34308         var imageContext = imageCanvas.getContext("2d");
34309         
34310         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34311         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34312         
34313         var center = imageCanvas.width / 2;
34314         
34315         imageContext.translate(center, center);
34316         
34317         imageContext.rotate(this.rotate * Math.PI / 180);
34318         
34319         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34320         
34321         var canvas = document.createElement("canvas");
34322         
34323         var context = canvas.getContext("2d");
34324                 
34325         canvas.width = this.minWidth;
34326         canvas.height = this.minHeight;
34327
34328         switch (this.rotate) {
34329             case 0 :
34330                 
34331                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34332                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34333                 
34334                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34335                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34336                 
34337                 var targetWidth = this.minWidth - 2 * x;
34338                 var targetHeight = this.minHeight - 2 * y;
34339                 
34340                 var scale = 1;
34341                 
34342                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34343                     scale = targetWidth / width;
34344                 }
34345                 
34346                 if(x > 0 && y == 0){
34347                     scale = targetHeight / height;
34348                 }
34349                 
34350                 if(x > 0 && y > 0){
34351                     scale = targetWidth / width;
34352                     
34353                     if(width < height){
34354                         scale = targetHeight / height;
34355                     }
34356                 }
34357                 
34358                 context.scale(scale, scale);
34359                 
34360                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34361                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34362
34363                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34364                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34365
34366                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34367                 
34368                 break;
34369             case 90 : 
34370                 
34371                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34372                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34373                 
34374                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34375                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34376                 
34377                 var targetWidth = this.minWidth - 2 * x;
34378                 var targetHeight = this.minHeight - 2 * y;
34379                 
34380                 var scale = 1;
34381                 
34382                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34383                     scale = targetWidth / width;
34384                 }
34385                 
34386                 if(x > 0 && y == 0){
34387                     scale = targetHeight / height;
34388                 }
34389                 
34390                 if(x > 0 && y > 0){
34391                     scale = targetWidth / width;
34392                     
34393                     if(width < height){
34394                         scale = targetHeight / height;
34395                     }
34396                 }
34397                 
34398                 context.scale(scale, scale);
34399                 
34400                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34401                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34402
34403                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34404                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34405                 
34406                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34407                 
34408                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34409                 
34410                 break;
34411             case 180 :
34412                 
34413                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34414                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34415                 
34416                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34417                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34418                 
34419                 var targetWidth = this.minWidth - 2 * x;
34420                 var targetHeight = this.minHeight - 2 * y;
34421                 
34422                 var scale = 1;
34423                 
34424                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34425                     scale = targetWidth / width;
34426                 }
34427                 
34428                 if(x > 0 && y == 0){
34429                     scale = targetHeight / height;
34430                 }
34431                 
34432                 if(x > 0 && y > 0){
34433                     scale = targetWidth / width;
34434                     
34435                     if(width < height){
34436                         scale = targetHeight / height;
34437                     }
34438                 }
34439                 
34440                 context.scale(scale, scale);
34441                 
34442                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34443                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34444
34445                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34446                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34447
34448                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34449                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34450                 
34451                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34452                 
34453                 break;
34454             case 270 :
34455                 
34456                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34457                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34458                 
34459                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34460                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34461                 
34462                 var targetWidth = this.minWidth - 2 * x;
34463                 var targetHeight = this.minHeight - 2 * y;
34464                 
34465                 var scale = 1;
34466                 
34467                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34468                     scale = targetWidth / width;
34469                 }
34470                 
34471                 if(x > 0 && y == 0){
34472                     scale = targetHeight / height;
34473                 }
34474                 
34475                 if(x > 0 && y > 0){
34476                     scale = targetWidth / width;
34477                     
34478                     if(width < height){
34479                         scale = targetHeight / height;
34480                     }
34481                 }
34482                 
34483                 context.scale(scale, scale);
34484                 
34485                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34486                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34487
34488                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34489                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34490                 
34491                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34492                 
34493                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34494                 
34495                 break;
34496             default : 
34497                 break;
34498         }
34499         
34500         this.cropData = canvas.toDataURL(this.cropType);
34501         
34502         if(this.fireEvent('crop', this, this.cropData) !== false){
34503             this.process(this.file, this.cropData);
34504         }
34505         
34506         return;
34507         
34508     },
34509     
34510     setThumbBoxSize : function()
34511     {
34512         var width, height;
34513         
34514         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34515             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34516             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34517             
34518             this.minWidth = width;
34519             this.minHeight = height;
34520             
34521             if(this.rotate == 90 || this.rotate == 270){
34522                 this.minWidth = height;
34523                 this.minHeight = width;
34524             }
34525         }
34526         
34527         height = 300;
34528         width = Math.ceil(this.minWidth * height / this.minHeight);
34529         
34530         if(this.minWidth > this.minHeight){
34531             width = 300;
34532             height = Math.ceil(this.minHeight * width / this.minWidth);
34533         }
34534         
34535         this.thumbEl.setStyle({
34536             width : width + 'px',
34537             height : height + 'px'
34538         });
34539
34540         return;
34541             
34542     },
34543     
34544     setThumbBoxPosition : function()
34545     {
34546         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34547         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34548         
34549         this.thumbEl.setLeft(x);
34550         this.thumbEl.setTop(y);
34551         
34552     },
34553     
34554     baseRotateLevel : function()
34555     {
34556         this.baseRotate = 1;
34557         
34558         if(
34559                 typeof(this.exif) != 'undefined' &&
34560                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34561                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34562         ){
34563             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34564         }
34565         
34566         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34567         
34568     },
34569     
34570     baseScaleLevel : function()
34571     {
34572         var width, height;
34573         
34574         if(this.isDocument){
34575             
34576             if(this.baseRotate == 6 || this.baseRotate == 8){
34577             
34578                 height = this.thumbEl.getHeight();
34579                 this.baseScale = height / this.imageEl.OriginWidth;
34580
34581                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34582                     width = this.thumbEl.getWidth();
34583                     this.baseScale = width / this.imageEl.OriginHeight;
34584                 }
34585
34586                 return;
34587             }
34588
34589             height = this.thumbEl.getHeight();
34590             this.baseScale = height / this.imageEl.OriginHeight;
34591
34592             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34593                 width = this.thumbEl.getWidth();
34594                 this.baseScale = width / this.imageEl.OriginWidth;
34595             }
34596
34597             return;
34598         }
34599         
34600         if(this.baseRotate == 6 || this.baseRotate == 8){
34601             
34602             width = this.thumbEl.getHeight();
34603             this.baseScale = width / this.imageEl.OriginHeight;
34604             
34605             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34606                 height = this.thumbEl.getWidth();
34607                 this.baseScale = height / this.imageEl.OriginHeight;
34608             }
34609             
34610             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34611                 height = this.thumbEl.getWidth();
34612                 this.baseScale = height / this.imageEl.OriginHeight;
34613                 
34614                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34615                     width = this.thumbEl.getHeight();
34616                     this.baseScale = width / this.imageEl.OriginWidth;
34617                 }
34618             }
34619             
34620             return;
34621         }
34622         
34623         width = this.thumbEl.getWidth();
34624         this.baseScale = width / this.imageEl.OriginWidth;
34625         
34626         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34627             height = this.thumbEl.getHeight();
34628             this.baseScale = height / this.imageEl.OriginHeight;
34629         }
34630         
34631         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34632             
34633             height = this.thumbEl.getHeight();
34634             this.baseScale = height / this.imageEl.OriginHeight;
34635             
34636             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34637                 width = this.thumbEl.getWidth();
34638                 this.baseScale = width / this.imageEl.OriginWidth;
34639             }
34640             
34641         }
34642         
34643         return;
34644     },
34645     
34646     getScaleLevel : function()
34647     {
34648         return this.baseScale * Math.pow(1.1, this.scale);
34649     },
34650     
34651     onTouchStart : function(e)
34652     {
34653         if(!this.canvasLoaded){
34654             this.beforeSelectFile(e);
34655             return;
34656         }
34657         
34658         var touches = e.browserEvent.touches;
34659         
34660         if(!touches){
34661             return;
34662         }
34663         
34664         if(touches.length == 1){
34665             this.onMouseDown(e);
34666             return;
34667         }
34668         
34669         if(touches.length != 2){
34670             return;
34671         }
34672         
34673         var coords = [];
34674         
34675         for(var i = 0, finger; finger = touches[i]; i++){
34676             coords.push(finger.pageX, finger.pageY);
34677         }
34678         
34679         var x = Math.pow(coords[0] - coords[2], 2);
34680         var y = Math.pow(coords[1] - coords[3], 2);
34681         
34682         this.startDistance = Math.sqrt(x + y);
34683         
34684         this.startScale = this.scale;
34685         
34686         this.pinching = true;
34687         this.dragable = false;
34688         
34689     },
34690     
34691     onTouchMove : function(e)
34692     {
34693         if(!this.pinching && !this.dragable){
34694             return;
34695         }
34696         
34697         var touches = e.browserEvent.touches;
34698         
34699         if(!touches){
34700             return;
34701         }
34702         
34703         if(this.dragable){
34704             this.onMouseMove(e);
34705             return;
34706         }
34707         
34708         var coords = [];
34709         
34710         for(var i = 0, finger; finger = touches[i]; i++){
34711             coords.push(finger.pageX, finger.pageY);
34712         }
34713         
34714         var x = Math.pow(coords[0] - coords[2], 2);
34715         var y = Math.pow(coords[1] - coords[3], 2);
34716         
34717         this.endDistance = Math.sqrt(x + y);
34718         
34719         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34720         
34721         if(!this.zoomable()){
34722             this.scale = this.startScale;
34723             return;
34724         }
34725         
34726         this.draw();
34727         
34728     },
34729     
34730     onTouchEnd : function(e)
34731     {
34732         this.pinching = false;
34733         this.dragable = false;
34734         
34735     },
34736     
34737     process : function(file, crop)
34738     {
34739         if(this.loadMask){
34740             this.maskEl.mask(this.loadingText);
34741         }
34742         
34743         this.xhr = new XMLHttpRequest();
34744         
34745         file.xhr = this.xhr;
34746
34747         this.xhr.open(this.method, this.url, true);
34748         
34749         var headers = {
34750             "Accept": "application/json",
34751             "Cache-Control": "no-cache",
34752             "X-Requested-With": "XMLHttpRequest"
34753         };
34754         
34755         for (var headerName in headers) {
34756             var headerValue = headers[headerName];
34757             if (headerValue) {
34758                 this.xhr.setRequestHeader(headerName, headerValue);
34759             }
34760         }
34761         
34762         var _this = this;
34763         
34764         this.xhr.onload = function()
34765         {
34766             _this.xhrOnLoad(_this.xhr);
34767         }
34768         
34769         this.xhr.onerror = function()
34770         {
34771             _this.xhrOnError(_this.xhr);
34772         }
34773         
34774         var formData = new FormData();
34775
34776         formData.append('returnHTML', 'NO');
34777         
34778         if(crop){
34779             formData.append('crop', crop);
34780         }
34781         
34782         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34783             formData.append(this.paramName, file, file.name);
34784         }
34785         
34786         if(typeof(file.filename) != 'undefined'){
34787             formData.append('filename', file.filename);
34788         }
34789         
34790         if(typeof(file.mimetype) != 'undefined'){
34791             formData.append('mimetype', file.mimetype);
34792         }
34793         
34794         if(this.fireEvent('arrange', this, formData) != false){
34795             this.xhr.send(formData);
34796         };
34797     },
34798     
34799     xhrOnLoad : function(xhr)
34800     {
34801         if(this.loadMask){
34802             this.maskEl.unmask();
34803         }
34804         
34805         if (xhr.readyState !== 4) {
34806             this.fireEvent('exception', this, xhr);
34807             return;
34808         }
34809
34810         var response = Roo.decode(xhr.responseText);
34811         
34812         if(!response.success){
34813             this.fireEvent('exception', this, xhr);
34814             return;
34815         }
34816         
34817         var response = Roo.decode(xhr.responseText);
34818         
34819         this.fireEvent('upload', this, response);
34820         
34821     },
34822     
34823     xhrOnError : function()
34824     {
34825         if(this.loadMask){
34826             this.maskEl.unmask();
34827         }
34828         
34829         Roo.log('xhr on error');
34830         
34831         var response = Roo.decode(xhr.responseText);
34832           
34833         Roo.log(response);
34834         
34835     },
34836     
34837     prepare : function(file)
34838     {   
34839         if(this.loadMask){
34840             this.maskEl.mask(this.loadingText);
34841         }
34842         
34843         this.file = false;
34844         this.exif = {};
34845         
34846         if(typeof(file) === 'string'){
34847             this.loadCanvas(file);
34848             return;
34849         }
34850         
34851         if(!file || !this.urlAPI){
34852             return;
34853         }
34854         
34855         this.file = file;
34856         this.cropType = file.type;
34857         
34858         var _this = this;
34859         
34860         if(this.fireEvent('prepare', this, this.file) != false){
34861             
34862             var reader = new FileReader();
34863             
34864             reader.onload = function (e) {
34865                 if (e.target.error) {
34866                     Roo.log(e.target.error);
34867                     return;
34868                 }
34869                 
34870                 var buffer = e.target.result,
34871                     dataView = new DataView(buffer),
34872                     offset = 2,
34873                     maxOffset = dataView.byteLength - 4,
34874                     markerBytes,
34875                     markerLength;
34876                 
34877                 if (dataView.getUint16(0) === 0xffd8) {
34878                     while (offset < maxOffset) {
34879                         markerBytes = dataView.getUint16(offset);
34880                         
34881                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34882                             markerLength = dataView.getUint16(offset + 2) + 2;
34883                             if (offset + markerLength > dataView.byteLength) {
34884                                 Roo.log('Invalid meta data: Invalid segment size.');
34885                                 break;
34886                             }
34887                             
34888                             if(markerBytes == 0xffe1){
34889                                 _this.parseExifData(
34890                                     dataView,
34891                                     offset,
34892                                     markerLength
34893                                 );
34894                             }
34895                             
34896                             offset += markerLength;
34897                             
34898                             continue;
34899                         }
34900                         
34901                         break;
34902                     }
34903                     
34904                 }
34905                 
34906                 var url = _this.urlAPI.createObjectURL(_this.file);
34907                 
34908                 _this.loadCanvas(url);
34909                 
34910                 return;
34911             }
34912             
34913             reader.readAsArrayBuffer(this.file);
34914             
34915         }
34916         
34917     },
34918     
34919     parseExifData : function(dataView, offset, length)
34920     {
34921         var tiffOffset = offset + 10,
34922             littleEndian,
34923             dirOffset;
34924     
34925         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34926             // No Exif data, might be XMP data instead
34927             return;
34928         }
34929         
34930         // Check for the ASCII code for "Exif" (0x45786966):
34931         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34932             // No Exif data, might be XMP data instead
34933             return;
34934         }
34935         if (tiffOffset + 8 > dataView.byteLength) {
34936             Roo.log('Invalid Exif data: Invalid segment size.');
34937             return;
34938         }
34939         // Check for the two null bytes:
34940         if (dataView.getUint16(offset + 8) !== 0x0000) {
34941             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34942             return;
34943         }
34944         // Check the byte alignment:
34945         switch (dataView.getUint16(tiffOffset)) {
34946         case 0x4949:
34947             littleEndian = true;
34948             break;
34949         case 0x4D4D:
34950             littleEndian = false;
34951             break;
34952         default:
34953             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34954             return;
34955         }
34956         // Check for the TIFF tag marker (0x002A):
34957         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34958             Roo.log('Invalid Exif data: Missing TIFF marker.');
34959             return;
34960         }
34961         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34962         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34963         
34964         this.parseExifTags(
34965             dataView,
34966             tiffOffset,
34967             tiffOffset + dirOffset,
34968             littleEndian
34969         );
34970     },
34971     
34972     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34973     {
34974         var tagsNumber,
34975             dirEndOffset,
34976             i;
34977         if (dirOffset + 6 > dataView.byteLength) {
34978             Roo.log('Invalid Exif data: Invalid directory offset.');
34979             return;
34980         }
34981         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34982         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34983         if (dirEndOffset + 4 > dataView.byteLength) {
34984             Roo.log('Invalid Exif data: Invalid directory size.');
34985             return;
34986         }
34987         for (i = 0; i < tagsNumber; i += 1) {
34988             this.parseExifTag(
34989                 dataView,
34990                 tiffOffset,
34991                 dirOffset + 2 + 12 * i, // tag offset
34992                 littleEndian
34993             );
34994         }
34995         // Return the offset to the next directory:
34996         return dataView.getUint32(dirEndOffset, littleEndian);
34997     },
34998     
34999     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
35000     {
35001         var tag = dataView.getUint16(offset, littleEndian);
35002         
35003         this.exif[tag] = this.getExifValue(
35004             dataView,
35005             tiffOffset,
35006             offset,
35007             dataView.getUint16(offset + 2, littleEndian), // tag type
35008             dataView.getUint32(offset + 4, littleEndian), // tag length
35009             littleEndian
35010         );
35011     },
35012     
35013     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35014     {
35015         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35016             tagSize,
35017             dataOffset,
35018             values,
35019             i,
35020             str,
35021             c;
35022     
35023         if (!tagType) {
35024             Roo.log('Invalid Exif data: Invalid tag type.');
35025             return;
35026         }
35027         
35028         tagSize = tagType.size * length;
35029         // Determine if the value is contained in the dataOffset bytes,
35030         // or if the value at the dataOffset is a pointer to the actual data:
35031         dataOffset = tagSize > 4 ?
35032                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35033         if (dataOffset + tagSize > dataView.byteLength) {
35034             Roo.log('Invalid Exif data: Invalid data offset.');
35035             return;
35036         }
35037         if (length === 1) {
35038             return tagType.getValue(dataView, dataOffset, littleEndian);
35039         }
35040         values = [];
35041         for (i = 0; i < length; i += 1) {
35042             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35043         }
35044         
35045         if (tagType.ascii) {
35046             str = '';
35047             // Concatenate the chars:
35048             for (i = 0; i < values.length; i += 1) {
35049                 c = values[i];
35050                 // Ignore the terminating NULL byte(s):
35051                 if (c === '\u0000') {
35052                     break;
35053                 }
35054                 str += c;
35055             }
35056             return str;
35057         }
35058         return values;
35059     }
35060     
35061 });
35062
35063 Roo.apply(Roo.bootstrap.UploadCropbox, {
35064     tags : {
35065         'Orientation': 0x0112
35066     },
35067     
35068     Orientation: {
35069             1: 0, //'top-left',
35070 //            2: 'top-right',
35071             3: 180, //'bottom-right',
35072 //            4: 'bottom-left',
35073 //            5: 'left-top',
35074             6: 90, //'right-top',
35075 //            7: 'right-bottom',
35076             8: 270 //'left-bottom'
35077     },
35078     
35079     exifTagTypes : {
35080         // byte, 8-bit unsigned int:
35081         1: {
35082             getValue: function (dataView, dataOffset) {
35083                 return dataView.getUint8(dataOffset);
35084             },
35085             size: 1
35086         },
35087         // ascii, 8-bit byte:
35088         2: {
35089             getValue: function (dataView, dataOffset) {
35090                 return String.fromCharCode(dataView.getUint8(dataOffset));
35091             },
35092             size: 1,
35093             ascii: true
35094         },
35095         // short, 16 bit int:
35096         3: {
35097             getValue: function (dataView, dataOffset, littleEndian) {
35098                 return dataView.getUint16(dataOffset, littleEndian);
35099             },
35100             size: 2
35101         },
35102         // long, 32 bit int:
35103         4: {
35104             getValue: function (dataView, dataOffset, littleEndian) {
35105                 return dataView.getUint32(dataOffset, littleEndian);
35106             },
35107             size: 4
35108         },
35109         // rational = two long values, first is numerator, second is denominator:
35110         5: {
35111             getValue: function (dataView, dataOffset, littleEndian) {
35112                 return dataView.getUint32(dataOffset, littleEndian) /
35113                     dataView.getUint32(dataOffset + 4, littleEndian);
35114             },
35115             size: 8
35116         },
35117         // slong, 32 bit signed int:
35118         9: {
35119             getValue: function (dataView, dataOffset, littleEndian) {
35120                 return dataView.getInt32(dataOffset, littleEndian);
35121             },
35122             size: 4
35123         },
35124         // srational, two slongs, first is numerator, second is denominator:
35125         10: {
35126             getValue: function (dataView, dataOffset, littleEndian) {
35127                 return dataView.getInt32(dataOffset, littleEndian) /
35128                     dataView.getInt32(dataOffset + 4, littleEndian);
35129             },
35130             size: 8
35131         }
35132     },
35133     
35134     footer : {
35135         STANDARD : [
35136             {
35137                 tag : 'div',
35138                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35139                 action : 'rotate-left',
35140                 cn : [
35141                     {
35142                         tag : 'button',
35143                         cls : 'btn btn-default',
35144                         html : '<i class="fa fa-undo"></i>'
35145                     }
35146                 ]
35147             },
35148             {
35149                 tag : 'div',
35150                 cls : 'btn-group roo-upload-cropbox-picture',
35151                 action : 'picture',
35152                 cn : [
35153                     {
35154                         tag : 'button',
35155                         cls : 'btn btn-default',
35156                         html : '<i class="fa fa-picture-o"></i>'
35157                     }
35158                 ]
35159             },
35160             {
35161                 tag : 'div',
35162                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35163                 action : 'rotate-right',
35164                 cn : [
35165                     {
35166                         tag : 'button',
35167                         cls : 'btn btn-default',
35168                         html : '<i class="fa fa-repeat"></i>'
35169                     }
35170                 ]
35171             }
35172         ],
35173         DOCUMENT : [
35174             {
35175                 tag : 'div',
35176                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35177                 action : 'rotate-left',
35178                 cn : [
35179                     {
35180                         tag : 'button',
35181                         cls : 'btn btn-default',
35182                         html : '<i class="fa fa-undo"></i>'
35183                     }
35184                 ]
35185             },
35186             {
35187                 tag : 'div',
35188                 cls : 'btn-group roo-upload-cropbox-download',
35189                 action : 'download',
35190                 cn : [
35191                     {
35192                         tag : 'button',
35193                         cls : 'btn btn-default',
35194                         html : '<i class="fa fa-download"></i>'
35195                     }
35196                 ]
35197             },
35198             {
35199                 tag : 'div',
35200                 cls : 'btn-group roo-upload-cropbox-crop',
35201                 action : 'crop',
35202                 cn : [
35203                     {
35204                         tag : 'button',
35205                         cls : 'btn btn-default',
35206                         html : '<i class="fa fa-crop"></i>'
35207                     }
35208                 ]
35209             },
35210             {
35211                 tag : 'div',
35212                 cls : 'btn-group roo-upload-cropbox-trash',
35213                 action : 'trash',
35214                 cn : [
35215                     {
35216                         tag : 'button',
35217                         cls : 'btn btn-default',
35218                         html : '<i class="fa fa-trash"></i>'
35219                     }
35220                 ]
35221             },
35222             {
35223                 tag : 'div',
35224                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35225                 action : 'rotate-right',
35226                 cn : [
35227                     {
35228                         tag : 'button',
35229                         cls : 'btn btn-default',
35230                         html : '<i class="fa fa-repeat"></i>'
35231                     }
35232                 ]
35233             }
35234         ],
35235         ROTATOR : [
35236             {
35237                 tag : 'div',
35238                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35239                 action : 'rotate-left',
35240                 cn : [
35241                     {
35242                         tag : 'button',
35243                         cls : 'btn btn-default',
35244                         html : '<i class="fa fa-undo"></i>'
35245                     }
35246                 ]
35247             },
35248             {
35249                 tag : 'div',
35250                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35251                 action : 'rotate-right',
35252                 cn : [
35253                     {
35254                         tag : 'button',
35255                         cls : 'btn btn-default',
35256                         html : '<i class="fa fa-repeat"></i>'
35257                     }
35258                 ]
35259             }
35260         ]
35261     }
35262 });
35263
35264 /*
35265 * Licence: LGPL
35266 */
35267
35268 /**
35269  * @class Roo.bootstrap.DocumentManager
35270  * @extends Roo.bootstrap.Component
35271  * Bootstrap DocumentManager class
35272  * @cfg {String} paramName default 'imageUpload'
35273  * @cfg {String} toolTipName default 'filename'
35274  * @cfg {String} method default POST
35275  * @cfg {String} url action url
35276  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35277  * @cfg {Boolean} multiple multiple upload default true
35278  * @cfg {Number} thumbSize default 300
35279  * @cfg {String} fieldLabel
35280  * @cfg {Number} labelWidth default 4
35281  * @cfg {String} labelAlign (left|top) default left
35282  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35283 * @cfg {Number} labellg set the width of label (1-12)
35284  * @cfg {Number} labelmd set the width of label (1-12)
35285  * @cfg {Number} labelsm set the width of label (1-12)
35286  * @cfg {Number} labelxs set the width of label (1-12)
35287  * 
35288  * @constructor
35289  * Create a new DocumentManager
35290  * @param {Object} config The config object
35291  */
35292
35293 Roo.bootstrap.DocumentManager = function(config){
35294     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35295     
35296     this.files = [];
35297     this.delegates = [];
35298     
35299     this.addEvents({
35300         /**
35301          * @event initial
35302          * Fire when initial the DocumentManager
35303          * @param {Roo.bootstrap.DocumentManager} this
35304          */
35305         "initial" : true,
35306         /**
35307          * @event inspect
35308          * inspect selected file
35309          * @param {Roo.bootstrap.DocumentManager} this
35310          * @param {File} file
35311          */
35312         "inspect" : true,
35313         /**
35314          * @event exception
35315          * Fire when xhr load exception
35316          * @param {Roo.bootstrap.DocumentManager} this
35317          * @param {XMLHttpRequest} xhr
35318          */
35319         "exception" : true,
35320         /**
35321          * @event afterupload
35322          * Fire when xhr load exception
35323          * @param {Roo.bootstrap.DocumentManager} this
35324          * @param {XMLHttpRequest} xhr
35325          */
35326         "afterupload" : true,
35327         /**
35328          * @event prepare
35329          * prepare the form data
35330          * @param {Roo.bootstrap.DocumentManager} this
35331          * @param {Object} formData
35332          */
35333         "prepare" : true,
35334         /**
35335          * @event remove
35336          * Fire when remove the file
35337          * @param {Roo.bootstrap.DocumentManager} this
35338          * @param {Object} file
35339          */
35340         "remove" : true,
35341         /**
35342          * @event refresh
35343          * Fire after refresh the file
35344          * @param {Roo.bootstrap.DocumentManager} this
35345          */
35346         "refresh" : true,
35347         /**
35348          * @event click
35349          * Fire after click the image
35350          * @param {Roo.bootstrap.DocumentManager} this
35351          * @param {Object} file
35352          */
35353         "click" : true,
35354         /**
35355          * @event edit
35356          * Fire when upload a image and editable set to true
35357          * @param {Roo.bootstrap.DocumentManager} this
35358          * @param {Object} file
35359          */
35360         "edit" : true,
35361         /**
35362          * @event beforeselectfile
35363          * Fire before select file
35364          * @param {Roo.bootstrap.DocumentManager} this
35365          */
35366         "beforeselectfile" : true,
35367         /**
35368          * @event process
35369          * Fire before process file
35370          * @param {Roo.bootstrap.DocumentManager} this
35371          * @param {Object} file
35372          */
35373         "process" : true,
35374         /**
35375          * @event previewrendered
35376          * Fire when preview rendered
35377          * @param {Roo.bootstrap.DocumentManager} this
35378          * @param {Object} file
35379          */
35380         "previewrendered" : true,
35381         /**
35382          */
35383         "previewResize" : true
35384         
35385     });
35386 };
35387
35388 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35389     
35390     boxes : 0,
35391     inputName : '',
35392     thumbSize : 300,
35393     multiple : true,
35394     files : false,
35395     method : 'POST',
35396     url : '',
35397     paramName : 'imageUpload',
35398     toolTipName : 'filename',
35399     fieldLabel : '',
35400     labelWidth : 4,
35401     labelAlign : 'left',
35402     editable : true,
35403     delegates : false,
35404     xhr : false, 
35405     
35406     labellg : 0,
35407     labelmd : 0,
35408     labelsm : 0,
35409     labelxs : 0,
35410     
35411     getAutoCreate : function()
35412     {   
35413         var managerWidget = {
35414             tag : 'div',
35415             cls : 'roo-document-manager',
35416             cn : [
35417                 {
35418                     tag : 'input',
35419                     cls : 'roo-document-manager-selector',
35420                     type : 'file'
35421                 },
35422                 {
35423                     tag : 'div',
35424                     cls : 'roo-document-manager-uploader',
35425                     cn : [
35426                         {
35427                             tag : 'div',
35428                             cls : 'roo-document-manager-upload-btn',
35429                             html : '<i class="fa fa-plus"></i>'
35430                         }
35431                     ]
35432                     
35433                 }
35434             ]
35435         };
35436         
35437         var content = [
35438             {
35439                 tag : 'div',
35440                 cls : 'column col-md-12',
35441                 cn : managerWidget
35442             }
35443         ];
35444         
35445         if(this.fieldLabel.length){
35446             
35447             content = [
35448                 {
35449                     tag : 'div',
35450                     cls : 'column col-md-12',
35451                     html : this.fieldLabel
35452                 },
35453                 {
35454                     tag : 'div',
35455                     cls : 'column col-md-12',
35456                     cn : managerWidget
35457                 }
35458             ];
35459
35460             if(this.labelAlign == 'left'){
35461                 content = [
35462                     {
35463                         tag : 'div',
35464                         cls : 'column',
35465                         html : this.fieldLabel
35466                     },
35467                     {
35468                         tag : 'div',
35469                         cls : 'column',
35470                         cn : managerWidget
35471                     }
35472                 ];
35473                 
35474                 if(this.labelWidth > 12){
35475                     content[0].style = "width: " + this.labelWidth + 'px';
35476                 }
35477
35478                 if(this.labelWidth < 13 && this.labelmd == 0){
35479                     this.labelmd = this.labelWidth;
35480                 }
35481
35482                 if(this.labellg > 0){
35483                     content[0].cls += ' col-lg-' + this.labellg;
35484                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35485                 }
35486
35487                 if(this.labelmd > 0){
35488                     content[0].cls += ' col-md-' + this.labelmd;
35489                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35490                 }
35491
35492                 if(this.labelsm > 0){
35493                     content[0].cls += ' col-sm-' + this.labelsm;
35494                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35495                 }
35496
35497                 if(this.labelxs > 0){
35498                     content[0].cls += ' col-xs-' + this.labelxs;
35499                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35500                 }
35501                 
35502             }
35503         }
35504         
35505         var cfg = {
35506             tag : 'div',
35507             cls : 'row clearfix',
35508             cn : content
35509         };
35510         
35511         return cfg;
35512         
35513     },
35514     
35515     initEvents : function()
35516     {
35517         this.managerEl = this.el.select('.roo-document-manager', true).first();
35518         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35519         
35520         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35521         this.selectorEl.hide();
35522         
35523         if(this.multiple){
35524             this.selectorEl.attr('multiple', 'multiple');
35525         }
35526         
35527         this.selectorEl.on('change', this.onFileSelected, this);
35528         
35529         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35530         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35531         
35532         this.uploader.on('click', this.onUploaderClick, this);
35533         
35534         this.renderProgressDialog();
35535         
35536         var _this = this;
35537         
35538         window.addEventListener("resize", function() { _this.refresh(); } );
35539         
35540         this.fireEvent('initial', this);
35541     },
35542     
35543     renderProgressDialog : function()
35544     {
35545         var _this = this;
35546         
35547         this.progressDialog = new Roo.bootstrap.Modal({
35548             cls : 'roo-document-manager-progress-dialog',
35549             allow_close : false,
35550             animate : false,
35551             title : '',
35552             buttons : [
35553                 {
35554                     name  :'cancel',
35555                     weight : 'danger',
35556                     html : 'Cancel'
35557                 }
35558             ], 
35559             listeners : { 
35560                 btnclick : function() {
35561                     _this.uploadCancel();
35562                     this.hide();
35563                 }
35564             }
35565         });
35566          
35567         this.progressDialog.render(Roo.get(document.body));
35568          
35569         this.progress = new Roo.bootstrap.Progress({
35570             cls : 'roo-document-manager-progress',
35571             active : true,
35572             striped : true
35573         });
35574         
35575         this.progress.render(this.progressDialog.getChildContainer());
35576         
35577         this.progressBar = new Roo.bootstrap.ProgressBar({
35578             cls : 'roo-document-manager-progress-bar',
35579             aria_valuenow : 0,
35580             aria_valuemin : 0,
35581             aria_valuemax : 12,
35582             panel : 'success'
35583         });
35584         
35585         this.progressBar.render(this.progress.getChildContainer());
35586     },
35587     
35588     onUploaderClick : function(e)
35589     {
35590         e.preventDefault();
35591      
35592         if(this.fireEvent('beforeselectfile', this) != false){
35593             this.selectorEl.dom.click();
35594         }
35595         
35596     },
35597     
35598     onFileSelected : function(e)
35599     {
35600         e.preventDefault();
35601         
35602         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35603             return;
35604         }
35605         
35606         Roo.each(this.selectorEl.dom.files, function(file){
35607             if(this.fireEvent('inspect', this, file) != false){
35608                 this.files.push(file);
35609             }
35610         }, this);
35611         
35612         this.queue();
35613         
35614     },
35615     
35616     queue : function()
35617     {
35618         this.selectorEl.dom.value = '';
35619         
35620         if(!this.files || !this.files.length){
35621             return;
35622         }
35623         
35624         if(this.boxes > 0 && this.files.length > this.boxes){
35625             this.files = this.files.slice(0, this.boxes);
35626         }
35627         
35628         this.uploader.show();
35629         
35630         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35631             this.uploader.hide();
35632         }
35633         
35634         var _this = this;
35635         
35636         var files = [];
35637         
35638         var docs = [];
35639         
35640         Roo.each(this.files, function(file){
35641             
35642             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35643                 var f = this.renderPreview(file);
35644                 files.push(f);
35645                 return;
35646             }
35647             
35648             if(file.type.indexOf('image') != -1){
35649                 this.delegates.push(
35650                     (function(){
35651                         _this.process(file);
35652                     }).createDelegate(this)
35653                 );
35654         
35655                 return;
35656             }
35657             
35658             docs.push(
35659                 (function(){
35660                     _this.process(file);
35661                 }).createDelegate(this)
35662             );
35663             
35664         }, this);
35665         
35666         this.files = files;
35667         
35668         this.delegates = this.delegates.concat(docs);
35669         
35670         if(!this.delegates.length){
35671             this.refresh();
35672             return;
35673         }
35674         
35675         this.progressBar.aria_valuemax = this.delegates.length;
35676         
35677         this.arrange();
35678         
35679         return;
35680     },
35681     
35682     arrange : function()
35683     {
35684         if(!this.delegates.length){
35685             this.progressDialog.hide();
35686             this.refresh();
35687             return;
35688         }
35689         
35690         var delegate = this.delegates.shift();
35691         
35692         this.progressDialog.show();
35693         
35694         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35695         
35696         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35697         
35698         delegate();
35699     },
35700     
35701     refresh : function()
35702     {
35703         this.uploader.show();
35704         
35705         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35706             this.uploader.hide();
35707         }
35708         
35709         Roo.isTouch ? this.closable(false) : this.closable(true);
35710         
35711         this.fireEvent('refresh', this);
35712     },
35713     
35714     onRemove : function(e, el, o)
35715     {
35716         e.preventDefault();
35717         
35718         this.fireEvent('remove', this, o);
35719         
35720     },
35721     
35722     remove : function(o)
35723     {
35724         var files = [];
35725         
35726         Roo.each(this.files, function(file){
35727             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35728                 files.push(file);
35729                 return;
35730             }
35731
35732             o.target.remove();
35733
35734         }, this);
35735         
35736         this.files = files;
35737         
35738         this.refresh();
35739     },
35740     
35741     clear : function()
35742     {
35743         Roo.each(this.files, function(file){
35744             if(!file.target){
35745                 return;
35746             }
35747             
35748             file.target.remove();
35749
35750         }, this);
35751         
35752         this.files = [];
35753         
35754         this.refresh();
35755     },
35756     
35757     onClick : function(e, el, o)
35758     {
35759         e.preventDefault();
35760         
35761         this.fireEvent('click', this, o);
35762         
35763     },
35764     
35765     closable : function(closable)
35766     {
35767         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35768             
35769             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35770             
35771             if(closable){
35772                 el.show();
35773                 return;
35774             }
35775             
35776             el.hide();
35777             
35778         }, this);
35779     },
35780     
35781     xhrOnLoad : function(xhr)
35782     {
35783         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35784             el.remove();
35785         }, this);
35786         
35787         if (xhr.readyState !== 4) {
35788             this.arrange();
35789             this.fireEvent('exception', this, xhr);
35790             return;
35791         }
35792
35793         var response = Roo.decode(xhr.responseText);
35794         
35795         if(!response.success){
35796             this.arrange();
35797             this.fireEvent('exception', this, xhr);
35798             return;
35799         }
35800         
35801         var file = this.renderPreview(response.data);
35802         
35803         this.files.push(file);
35804         
35805         this.arrange();
35806         
35807         this.fireEvent('afterupload', this, xhr);
35808         
35809     },
35810     
35811     xhrOnError : function(xhr)
35812     {
35813         Roo.log('xhr on error');
35814         
35815         var response = Roo.decode(xhr.responseText);
35816           
35817         Roo.log(response);
35818         
35819         this.arrange();
35820     },
35821     
35822     process : function(file)
35823     {
35824         if(this.fireEvent('process', this, file) !== false){
35825             if(this.editable && file.type.indexOf('image') != -1){
35826                 this.fireEvent('edit', this, file);
35827                 return;
35828             }
35829
35830             this.uploadStart(file, false);
35831
35832             return;
35833         }
35834         
35835     },
35836     
35837     uploadStart : function(file, crop)
35838     {
35839         this.xhr = new XMLHttpRequest();
35840         
35841         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35842             this.arrange();
35843             return;
35844         }
35845         
35846         file.xhr = this.xhr;
35847             
35848         this.managerEl.createChild({
35849             tag : 'div',
35850             cls : 'roo-document-manager-loading',
35851             cn : [
35852                 {
35853                     tag : 'div',
35854                     tooltip : file.name,
35855                     cls : 'roo-document-manager-thumb',
35856                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35857                 }
35858             ]
35859
35860         });
35861
35862         this.xhr.open(this.method, this.url, true);
35863         
35864         var headers = {
35865             "Accept": "application/json",
35866             "Cache-Control": "no-cache",
35867             "X-Requested-With": "XMLHttpRequest"
35868         };
35869         
35870         for (var headerName in headers) {
35871             var headerValue = headers[headerName];
35872             if (headerValue) {
35873                 this.xhr.setRequestHeader(headerName, headerValue);
35874             }
35875         }
35876         
35877         var _this = this;
35878         
35879         this.xhr.onload = function()
35880         {
35881             _this.xhrOnLoad(_this.xhr);
35882         }
35883         
35884         this.xhr.onerror = function()
35885         {
35886             _this.xhrOnError(_this.xhr);
35887         }
35888         
35889         var formData = new FormData();
35890
35891         formData.append('returnHTML', 'NO');
35892         
35893         if(crop){
35894             formData.append('crop', crop);
35895         }
35896         
35897         formData.append(this.paramName, file, file.name);
35898         
35899         var options = {
35900             file : file, 
35901             manually : false
35902         };
35903         
35904         if(this.fireEvent('prepare', this, formData, options) != false){
35905             
35906             if(options.manually){
35907                 return;
35908             }
35909             
35910             this.xhr.send(formData);
35911             return;
35912         };
35913         
35914         this.uploadCancel();
35915     },
35916     
35917     uploadCancel : function()
35918     {
35919         if (this.xhr) {
35920             this.xhr.abort();
35921         }
35922         
35923         this.delegates = [];
35924         
35925         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35926             el.remove();
35927         }, this);
35928         
35929         this.arrange();
35930     },
35931     
35932     renderPreview : function(file)
35933     {
35934         if(typeof(file.target) != 'undefined' && file.target){
35935             return file;
35936         }
35937         
35938         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35939         
35940         var previewEl = this.managerEl.createChild({
35941             tag : 'div',
35942             cls : 'roo-document-manager-preview',
35943             cn : [
35944                 {
35945                     tag : 'div',
35946                     tooltip : file[this.toolTipName],
35947                     cls : 'roo-document-manager-thumb',
35948                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35949                 },
35950                 {
35951                     tag : 'button',
35952                     cls : 'close',
35953                     html : '<i class="fa fa-times-circle"></i>'
35954                 }
35955             ]
35956         });
35957
35958         var close = previewEl.select('button.close', true).first();
35959
35960         close.on('click', this.onRemove, this, file);
35961
35962         file.target = previewEl;
35963
35964         var image = previewEl.select('img', true).first();
35965         
35966         var _this = this;
35967         
35968         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35969         
35970         image.on('click', this.onClick, this, file);
35971         
35972         this.fireEvent('previewrendered', this, file);
35973         
35974         return file;
35975         
35976     },
35977     
35978     onPreviewLoad : function(file, image)
35979     {
35980         if(typeof(file.target) == 'undefined' || !file.target){
35981             return;
35982         }
35983         
35984         var width = image.dom.naturalWidth || image.dom.width;
35985         var height = image.dom.naturalHeight || image.dom.height;
35986         
35987         if(!this.previewResize) {
35988             return;
35989         }
35990         
35991         if(width > height){
35992             file.target.addClass('wide');
35993             return;
35994         }
35995         
35996         file.target.addClass('tall');
35997         return;
35998         
35999     },
36000     
36001     uploadFromSource : function(file, crop)
36002     {
36003         this.xhr = new XMLHttpRequest();
36004         
36005         this.managerEl.createChild({
36006             tag : 'div',
36007             cls : 'roo-document-manager-loading',
36008             cn : [
36009                 {
36010                     tag : 'div',
36011                     tooltip : file.name,
36012                     cls : 'roo-document-manager-thumb',
36013                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36014                 }
36015             ]
36016
36017         });
36018
36019         this.xhr.open(this.method, this.url, true);
36020         
36021         var headers = {
36022             "Accept": "application/json",
36023             "Cache-Control": "no-cache",
36024             "X-Requested-With": "XMLHttpRequest"
36025         };
36026         
36027         for (var headerName in headers) {
36028             var headerValue = headers[headerName];
36029             if (headerValue) {
36030                 this.xhr.setRequestHeader(headerName, headerValue);
36031             }
36032         }
36033         
36034         var _this = this;
36035         
36036         this.xhr.onload = function()
36037         {
36038             _this.xhrOnLoad(_this.xhr);
36039         }
36040         
36041         this.xhr.onerror = function()
36042         {
36043             _this.xhrOnError(_this.xhr);
36044         }
36045         
36046         var formData = new FormData();
36047
36048         formData.append('returnHTML', 'NO');
36049         
36050         formData.append('crop', crop);
36051         
36052         if(typeof(file.filename) != 'undefined'){
36053             formData.append('filename', file.filename);
36054         }
36055         
36056         if(typeof(file.mimetype) != 'undefined'){
36057             formData.append('mimetype', file.mimetype);
36058         }
36059         
36060         Roo.log(formData);
36061         
36062         if(this.fireEvent('prepare', this, formData) != false){
36063             this.xhr.send(formData);
36064         };
36065     }
36066 });
36067
36068 /*
36069 * Licence: LGPL
36070 */
36071
36072 /**
36073  * @class Roo.bootstrap.DocumentViewer
36074  * @extends Roo.bootstrap.Component
36075  * Bootstrap DocumentViewer class
36076  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36077  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36078  * 
36079  * @constructor
36080  * Create a new DocumentViewer
36081  * @param {Object} config The config object
36082  */
36083
36084 Roo.bootstrap.DocumentViewer = function(config){
36085     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36086     
36087     this.addEvents({
36088         /**
36089          * @event initial
36090          * Fire after initEvent
36091          * @param {Roo.bootstrap.DocumentViewer} this
36092          */
36093         "initial" : true,
36094         /**
36095          * @event click
36096          * Fire after click
36097          * @param {Roo.bootstrap.DocumentViewer} this
36098          */
36099         "click" : true,
36100         /**
36101          * @event download
36102          * Fire after download button
36103          * @param {Roo.bootstrap.DocumentViewer} this
36104          */
36105         "download" : true,
36106         /**
36107          * @event trash
36108          * Fire after trash button
36109          * @param {Roo.bootstrap.DocumentViewer} this
36110          */
36111         "trash" : true
36112         
36113     });
36114 };
36115
36116 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36117     
36118     showDownload : true,
36119     
36120     showTrash : true,
36121     
36122     getAutoCreate : function()
36123     {
36124         var cfg = {
36125             tag : 'div',
36126             cls : 'roo-document-viewer',
36127             cn : [
36128                 {
36129                     tag : 'div',
36130                     cls : 'roo-document-viewer-body',
36131                     cn : [
36132                         {
36133                             tag : 'div',
36134                             cls : 'roo-document-viewer-thumb',
36135                             cn : [
36136                                 {
36137                                     tag : 'img',
36138                                     cls : 'roo-document-viewer-image'
36139                                 }
36140                             ]
36141                         }
36142                     ]
36143                 },
36144                 {
36145                     tag : 'div',
36146                     cls : 'roo-document-viewer-footer',
36147                     cn : {
36148                         tag : 'div',
36149                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36150                         cn : [
36151                             {
36152                                 tag : 'div',
36153                                 cls : 'btn-group roo-document-viewer-download',
36154                                 cn : [
36155                                     {
36156                                         tag : 'button',
36157                                         cls : 'btn btn-default',
36158                                         html : '<i class="fa fa-download"></i>'
36159                                     }
36160                                 ]
36161                             },
36162                             {
36163                                 tag : 'div',
36164                                 cls : 'btn-group roo-document-viewer-trash',
36165                                 cn : [
36166                                     {
36167                                         tag : 'button',
36168                                         cls : 'btn btn-default',
36169                                         html : '<i class="fa fa-trash"></i>'
36170                                     }
36171                                 ]
36172                             }
36173                         ]
36174                     }
36175                 }
36176             ]
36177         };
36178         
36179         return cfg;
36180     },
36181     
36182     initEvents : function()
36183     {
36184         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36185         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36186         
36187         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36188         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36189         
36190         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36191         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36192         
36193         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36194         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36195         
36196         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36197         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36198         
36199         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36200         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36201         
36202         this.bodyEl.on('click', this.onClick, this);
36203         this.downloadBtn.on('click', this.onDownload, this);
36204         this.trashBtn.on('click', this.onTrash, this);
36205         
36206         this.downloadBtn.hide();
36207         this.trashBtn.hide();
36208         
36209         if(this.showDownload){
36210             this.downloadBtn.show();
36211         }
36212         
36213         if(this.showTrash){
36214             this.trashBtn.show();
36215         }
36216         
36217         if(!this.showDownload && !this.showTrash) {
36218             this.footerEl.hide();
36219         }
36220         
36221     },
36222     
36223     initial : function()
36224     {
36225         this.fireEvent('initial', this);
36226         
36227     },
36228     
36229     onClick : function(e)
36230     {
36231         e.preventDefault();
36232         
36233         this.fireEvent('click', this);
36234     },
36235     
36236     onDownload : function(e)
36237     {
36238         e.preventDefault();
36239         
36240         this.fireEvent('download', this);
36241     },
36242     
36243     onTrash : function(e)
36244     {
36245         e.preventDefault();
36246         
36247         this.fireEvent('trash', this);
36248     }
36249     
36250 });
36251 /*
36252  * - LGPL
36253  *
36254  * FieldLabel
36255  * 
36256  */
36257
36258 /**
36259  * @class Roo.bootstrap.form.FieldLabel
36260  * @extends Roo.bootstrap.Component
36261  * Bootstrap FieldLabel class
36262  * @cfg {String} html contents of the element
36263  * @cfg {String} tag tag of the element default label
36264  * @cfg {String} cls class of the element
36265  * @cfg {String} target label target 
36266  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36267  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36268  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36269  * @cfg {String} iconTooltip default "This field is required"
36270  * @cfg {String} indicatorpos (left|right) default left
36271  * 
36272  * @constructor
36273  * Create a new FieldLabel
36274  * @param {Object} config The config object
36275  */
36276
36277 Roo.bootstrap.form.FieldLabel = function(config){
36278     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36279     
36280     this.addEvents({
36281             /**
36282              * @event invalid
36283              * Fires after the field has been marked as invalid.
36284              * @param {Roo.form.FieldLabel} this
36285              * @param {String} msg The validation message
36286              */
36287             invalid : true,
36288             /**
36289              * @event valid
36290              * Fires after the field has been validated with no errors.
36291              * @param {Roo.form.FieldLabel} this
36292              */
36293             valid : true
36294         });
36295 };
36296
36297 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36298     
36299     tag: 'label',
36300     cls: '',
36301     html: '',
36302     target: '',
36303     allowBlank : true,
36304     invalidClass : 'has-warning',
36305     validClass : 'has-success',
36306     iconTooltip : 'This field is required',
36307     indicatorpos : 'left',
36308     
36309     getAutoCreate : function(){
36310         
36311         var cls = "";
36312         if (!this.allowBlank) {
36313             cls  = "visible";
36314         }
36315         
36316         var cfg = {
36317             tag : this.tag,
36318             cls : 'roo-bootstrap-field-label ' + this.cls,
36319             for : this.target,
36320             cn : [
36321                 {
36322                     tag : 'i',
36323                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36324                     tooltip : this.iconTooltip
36325                 },
36326                 {
36327                     tag : 'span',
36328                     html : this.html
36329                 }
36330             ] 
36331         };
36332         
36333         if(this.indicatorpos == 'right'){
36334             var cfg = {
36335                 tag : this.tag,
36336                 cls : 'roo-bootstrap-field-label ' + this.cls,
36337                 for : this.target,
36338                 cn : [
36339                     {
36340                         tag : 'span',
36341                         html : this.html
36342                     },
36343                     {
36344                         tag : 'i',
36345                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36346                         tooltip : this.iconTooltip
36347                     }
36348                 ] 
36349             };
36350         }
36351         
36352         return cfg;
36353     },
36354     
36355     initEvents: function() 
36356     {
36357         Roo.bootstrap.Element.superclass.initEvents.call(this);
36358         
36359         this.indicator = this.indicatorEl();
36360         
36361         if(this.indicator){
36362             this.indicator.removeClass('visible');
36363             this.indicator.addClass('invisible');
36364         }
36365         
36366         Roo.bootstrap.form.FieldLabel.register(this);
36367     },
36368     
36369     indicatorEl : function()
36370     {
36371         var indicator = this.el.select('i.roo-required-indicator',true).first();
36372         
36373         if(!indicator){
36374             return false;
36375         }
36376         
36377         return indicator;
36378         
36379     },
36380     
36381     /**
36382      * Mark this field as valid
36383      */
36384     markValid : function()
36385     {
36386         if(this.indicator){
36387             this.indicator.removeClass('visible');
36388             this.indicator.addClass('invisible');
36389         }
36390         if (Roo.bootstrap.version == 3) {
36391             this.el.removeClass(this.invalidClass);
36392             this.el.addClass(this.validClass);
36393         } else {
36394             this.el.removeClass('is-invalid');
36395             this.el.addClass('is-valid');
36396         }
36397         
36398         
36399         this.fireEvent('valid', this);
36400     },
36401     
36402     /**
36403      * Mark this field as invalid
36404      * @param {String} msg The validation message
36405      */
36406     markInvalid : function(msg)
36407     {
36408         if(this.indicator){
36409             this.indicator.removeClass('invisible');
36410             this.indicator.addClass('visible');
36411         }
36412           if (Roo.bootstrap.version == 3) {
36413             this.el.removeClass(this.validClass);
36414             this.el.addClass(this.invalidClass);
36415         } else {
36416             this.el.removeClass('is-valid');
36417             this.el.addClass('is-invalid');
36418         }
36419         
36420         
36421         this.fireEvent('invalid', this, msg);
36422     }
36423     
36424    
36425 });
36426
36427 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36428     
36429     groups: {},
36430     
36431      /**
36432     * register a FieldLabel Group
36433     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36434     */
36435     register : function(label)
36436     {
36437         if(this.groups.hasOwnProperty(label.target)){
36438             return;
36439         }
36440      
36441         this.groups[label.target] = label;
36442         
36443     },
36444     /**
36445     * fetch a FieldLabel Group based on the target
36446     * @param {string} target
36447     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36448     */
36449     get: function(target) {
36450         if (typeof(this.groups[target]) == 'undefined') {
36451             return false;
36452         }
36453         
36454         return this.groups[target] ;
36455     }
36456 });
36457
36458  
36459
36460  /*
36461  * - LGPL
36462  *
36463  * page DateSplitField.
36464  * 
36465  */
36466
36467
36468 /**
36469  * @class Roo.bootstrap.form.DateSplitField
36470  * @extends Roo.bootstrap.Component
36471  * Bootstrap DateSplitField class
36472  * @cfg {string} fieldLabel - the label associated
36473  * @cfg {Number} labelWidth set the width of label (0-12)
36474  * @cfg {String} labelAlign (top|left)
36475  * @cfg {Boolean} dayAllowBlank (true|false) default false
36476  * @cfg {Boolean} monthAllowBlank (true|false) default false
36477  * @cfg {Boolean} yearAllowBlank (true|false) default false
36478  * @cfg {string} dayPlaceholder 
36479  * @cfg {string} monthPlaceholder
36480  * @cfg {string} yearPlaceholder
36481  * @cfg {string} dayFormat default 'd'
36482  * @cfg {string} monthFormat default 'm'
36483  * @cfg {string} yearFormat default 'Y'
36484  * @cfg {Number} labellg set the width of label (1-12)
36485  * @cfg {Number} labelmd set the width of label (1-12)
36486  * @cfg {Number} labelsm set the width of label (1-12)
36487  * @cfg {Number} labelxs set the width of label (1-12)
36488
36489  *     
36490  * @constructor
36491  * Create a new DateSplitField
36492  * @param {Object} config The config object
36493  */
36494
36495 Roo.bootstrap.form.DateSplitField = function(config){
36496     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36497     
36498     this.addEvents({
36499         // raw events
36500          /**
36501          * @event years
36502          * getting the data of years
36503          * @param {Roo.bootstrap.form.DateSplitField} this
36504          * @param {Object} years
36505          */
36506         "years" : true,
36507         /**
36508          * @event days
36509          * getting the data of days
36510          * @param {Roo.bootstrap.form.DateSplitField} this
36511          * @param {Object} days
36512          */
36513         "days" : true,
36514         /**
36515          * @event invalid
36516          * Fires after the field has been marked as invalid.
36517          * @param {Roo.form.Field} this
36518          * @param {String} msg The validation message
36519          */
36520         invalid : true,
36521        /**
36522          * @event valid
36523          * Fires after the field has been validated with no errors.
36524          * @param {Roo.form.Field} this
36525          */
36526         valid : true
36527     });
36528 };
36529
36530 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36531     
36532     fieldLabel : '',
36533     labelAlign : 'top',
36534     labelWidth : 3,
36535     dayAllowBlank : false,
36536     monthAllowBlank : false,
36537     yearAllowBlank : false,
36538     dayPlaceholder : '',
36539     monthPlaceholder : '',
36540     yearPlaceholder : '',
36541     dayFormat : 'd',
36542     monthFormat : 'm',
36543     yearFormat : 'Y',
36544     isFormField : true,
36545     labellg : 0,
36546     labelmd : 0,
36547     labelsm : 0,
36548     labelxs : 0,
36549     
36550     getAutoCreate : function()
36551     {
36552         var cfg = {
36553             tag : 'div',
36554             cls : 'row roo-date-split-field-group',
36555             cn : [
36556                 {
36557                     tag : 'input',
36558                     type : 'hidden',
36559                     cls : 'form-hidden-field roo-date-split-field-group-value',
36560                     name : this.name
36561                 }
36562             ]
36563         };
36564         
36565         var labelCls = 'col-md-12';
36566         var contentCls = 'col-md-4';
36567         
36568         if(this.fieldLabel){
36569             
36570             var label = {
36571                 tag : 'div',
36572                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36573                 cn : [
36574                     {
36575                         tag : 'label',
36576                         html : this.fieldLabel
36577                     }
36578                 ]
36579             };
36580             
36581             if(this.labelAlign == 'left'){
36582             
36583                 if(this.labelWidth > 12){
36584                     label.style = "width: " + this.labelWidth + 'px';
36585                 }
36586
36587                 if(this.labelWidth < 13 && this.labelmd == 0){
36588                     this.labelmd = this.labelWidth;
36589                 }
36590
36591                 if(this.labellg > 0){
36592                     labelCls = ' col-lg-' + this.labellg;
36593                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36594                 }
36595
36596                 if(this.labelmd > 0){
36597                     labelCls = ' col-md-' + this.labelmd;
36598                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36599                 }
36600
36601                 if(this.labelsm > 0){
36602                     labelCls = ' col-sm-' + this.labelsm;
36603                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36604                 }
36605
36606                 if(this.labelxs > 0){
36607                     labelCls = ' col-xs-' + this.labelxs;
36608                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36609                 }
36610             }
36611             
36612             label.cls += ' ' + labelCls;
36613             
36614             cfg.cn.push(label);
36615         }
36616         
36617         Roo.each(['day', 'month', 'year'], function(t){
36618             cfg.cn.push({
36619                 tag : 'div',
36620                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36621             });
36622         }, this);
36623         
36624         return cfg;
36625     },
36626     
36627     inputEl: function ()
36628     {
36629         return this.el.select('.roo-date-split-field-group-value', true).first();
36630     },
36631     
36632     onRender : function(ct, position) 
36633     {
36634         var _this = this;
36635         
36636         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36637         
36638         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36639         
36640         this.dayField = new Roo.bootstrap.form.ComboBox({
36641             allowBlank : this.dayAllowBlank,
36642             alwaysQuery : true,
36643             displayField : 'value',
36644             editable : false,
36645             fieldLabel : '',
36646             forceSelection : true,
36647             mode : 'local',
36648             placeholder : this.dayPlaceholder,
36649             selectOnFocus : true,
36650             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36651             triggerAction : 'all',
36652             typeAhead : true,
36653             valueField : 'value',
36654             store : new Roo.data.SimpleStore({
36655                 data : (function() {    
36656                     var days = [];
36657                     _this.fireEvent('days', _this, days);
36658                     return days;
36659                 })(),
36660                 fields : [ 'value' ]
36661             }),
36662             listeners : {
36663                 select : function (_self, record, index)
36664                 {
36665                     _this.setValue(_this.getValue());
36666                 }
36667             }
36668         });
36669
36670         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36671         
36672         this.monthField = new Roo.bootstrap.form.MonthField({
36673             after : '<i class=\"fa fa-calendar\"></i>',
36674             allowBlank : this.monthAllowBlank,
36675             placeholder : this.monthPlaceholder,
36676             readOnly : true,
36677             listeners : {
36678                 render : function (_self)
36679                 {
36680                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36681                         e.preventDefault();
36682                         _self.focus();
36683                     });
36684                 },
36685                 select : function (_self, oldvalue, newvalue)
36686                 {
36687                     _this.setValue(_this.getValue());
36688                 }
36689             }
36690         });
36691         
36692         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36693         
36694         this.yearField = new Roo.bootstrap.form.ComboBox({
36695             allowBlank : this.yearAllowBlank,
36696             alwaysQuery : true,
36697             displayField : 'value',
36698             editable : false,
36699             fieldLabel : '',
36700             forceSelection : true,
36701             mode : 'local',
36702             placeholder : this.yearPlaceholder,
36703             selectOnFocus : true,
36704             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36705             triggerAction : 'all',
36706             typeAhead : true,
36707             valueField : 'value',
36708             store : new Roo.data.SimpleStore({
36709                 data : (function() {
36710                     var years = [];
36711                     _this.fireEvent('years', _this, years);
36712                     return years;
36713                 })(),
36714                 fields : [ 'value' ]
36715             }),
36716             listeners : {
36717                 select : function (_self, record, index)
36718                 {
36719                     _this.setValue(_this.getValue());
36720                 }
36721             }
36722         });
36723
36724         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36725     },
36726     
36727     setValue : function(v, format)
36728     {
36729         this.inputEl.dom.value = v;
36730         
36731         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36732         
36733         var d = Date.parseDate(v, f);
36734         
36735         if(!d){
36736             this.validate();
36737             return;
36738         }
36739         
36740         this.setDay(d.format(this.dayFormat));
36741         this.setMonth(d.format(this.monthFormat));
36742         this.setYear(d.format(this.yearFormat));
36743         
36744         this.validate();
36745         
36746         return;
36747     },
36748     
36749     setDay : function(v)
36750     {
36751         this.dayField.setValue(v);
36752         this.inputEl.dom.value = this.getValue();
36753         this.validate();
36754         return;
36755     },
36756     
36757     setMonth : function(v)
36758     {
36759         this.monthField.setValue(v, true);
36760         this.inputEl.dom.value = this.getValue();
36761         this.validate();
36762         return;
36763     },
36764     
36765     setYear : function(v)
36766     {
36767         this.yearField.setValue(v);
36768         this.inputEl.dom.value = this.getValue();
36769         this.validate();
36770         return;
36771     },
36772     
36773     getDay : function()
36774     {
36775         return this.dayField.getValue();
36776     },
36777     
36778     getMonth : function()
36779     {
36780         return this.monthField.getValue();
36781     },
36782     
36783     getYear : function()
36784     {
36785         return this.yearField.getValue();
36786     },
36787     
36788     getValue : function()
36789     {
36790         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36791         
36792         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36793         
36794         return date;
36795     },
36796     
36797     reset : function()
36798     {
36799         this.setDay('');
36800         this.setMonth('');
36801         this.setYear('');
36802         this.inputEl.dom.value = '';
36803         this.validate();
36804         return;
36805     },
36806     
36807     validate : function()
36808     {
36809         var d = this.dayField.validate();
36810         var m = this.monthField.validate();
36811         var y = this.yearField.validate();
36812         
36813         var valid = true;
36814         
36815         if(
36816                 (!this.dayAllowBlank && !d) ||
36817                 (!this.monthAllowBlank && !m) ||
36818                 (!this.yearAllowBlank && !y)
36819         ){
36820             valid = false;
36821         }
36822         
36823         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36824             return valid;
36825         }
36826         
36827         if(valid){
36828             this.markValid();
36829             return valid;
36830         }
36831         
36832         this.markInvalid();
36833         
36834         return valid;
36835     },
36836     
36837     markValid : function()
36838     {
36839         
36840         var label = this.el.select('label', true).first();
36841         var icon = this.el.select('i.fa-star', true).first();
36842
36843         if(label && icon){
36844             icon.remove();
36845         }
36846         
36847         this.fireEvent('valid', this);
36848     },
36849     
36850      /**
36851      * Mark this field as invalid
36852      * @param {String} msg The validation message
36853      */
36854     markInvalid : function(msg)
36855     {
36856         
36857         var label = this.el.select('label', true).first();
36858         var icon = this.el.select('i.fa-star', true).first();
36859
36860         if(label && !icon){
36861             this.el.select('.roo-date-split-field-label', true).createChild({
36862                 tag : 'i',
36863                 cls : 'text-danger fa fa-lg fa-star',
36864                 tooltip : 'This field is required',
36865                 style : 'margin-right:5px;'
36866             }, label, true);
36867         }
36868         
36869         this.fireEvent('invalid', this, msg);
36870     },
36871     
36872     clearInvalid : function()
36873     {
36874         var label = this.el.select('label', true).first();
36875         var icon = this.el.select('i.fa-star', true).first();
36876
36877         if(label && icon){
36878             icon.remove();
36879         }
36880         
36881         this.fireEvent('valid', this);
36882     },
36883     
36884     getName: function()
36885     {
36886         return this.name;
36887     }
36888     
36889 });
36890
36891  
36892
36893 /**
36894  * @class Roo.bootstrap.LayoutMasonry
36895  * @extends Roo.bootstrap.Component
36896  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36897  * Bootstrap Layout Masonry class
36898  *
36899  * This is based on 
36900  * http://masonry.desandro.com
36901  *
36902  * The idea is to render all the bricks based on vertical width...
36903  *
36904  * The original code extends 'outlayer' - we might need to use that....
36905
36906  * @constructor
36907  * Create a new Element
36908  * @param {Object} config The config object
36909  */
36910
36911 Roo.bootstrap.LayoutMasonry = function(config){
36912     
36913     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36914     
36915     this.bricks = [];
36916     
36917     Roo.bootstrap.LayoutMasonry.register(this);
36918     
36919     this.addEvents({
36920         // raw events
36921         /**
36922          * @event layout
36923          * Fire after layout the items
36924          * @param {Roo.bootstrap.LayoutMasonry} this
36925          * @param {Roo.EventObject} e
36926          */
36927         "layout" : true
36928     });
36929     
36930 };
36931
36932 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36933     
36934     /**
36935      * @cfg {Boolean} isLayoutInstant = no animation?
36936      */   
36937     isLayoutInstant : false, // needed?
36938    
36939     /**
36940      * @cfg {Number} boxWidth  width of the columns
36941      */   
36942     boxWidth : 450,
36943     
36944       /**
36945      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36946      */   
36947     boxHeight : 0,
36948     
36949     /**
36950      * @cfg {Number} padWidth padding below box..
36951      */   
36952     padWidth : 10, 
36953     
36954     /**
36955      * @cfg {Number} gutter gutter width..
36956      */   
36957     gutter : 10,
36958     
36959      /**
36960      * @cfg {Number} maxCols maximum number of columns
36961      */   
36962     
36963     maxCols: 0,
36964     
36965     /**
36966      * @cfg {Boolean} isAutoInitial defalut true
36967      */   
36968     isAutoInitial : true, 
36969     
36970     containerWidth: 0,
36971     
36972     /**
36973      * @cfg {Boolean} isHorizontal defalut false
36974      */   
36975     isHorizontal : false, 
36976
36977     currentSize : null,
36978     
36979     tag: 'div',
36980     
36981     cls: '',
36982     
36983     bricks: null, //CompositeElement
36984     
36985     cols : 1,
36986     
36987     _isLayoutInited : false,
36988     
36989 //    isAlternative : false, // only use for vertical layout...
36990     
36991     /**
36992      * @cfg {Number} alternativePadWidth padding below box..
36993      */   
36994     alternativePadWidth : 50,
36995     
36996     selectedBrick : [],
36997     
36998     getAutoCreate : function(){
36999         
37000         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
37001         
37002         var cfg = {
37003             tag: this.tag,
37004             cls: 'blog-masonary-wrapper ' + this.cls,
37005             cn : {
37006                 cls : 'mas-boxes masonary'
37007             }
37008         };
37009         
37010         return cfg;
37011     },
37012     
37013     getChildContainer: function( )
37014     {
37015         if (this.boxesEl) {
37016             return this.boxesEl;
37017         }
37018         
37019         this.boxesEl = this.el.select('.mas-boxes').first();
37020         
37021         return this.boxesEl;
37022     },
37023     
37024     
37025     initEvents : function()
37026     {
37027         var _this = this;
37028         
37029         if(this.isAutoInitial){
37030             Roo.log('hook children rendered');
37031             this.on('childrenrendered', function() {
37032                 Roo.log('children rendered');
37033                 _this.initial();
37034             } ,this);
37035         }
37036     },
37037     
37038     initial : function()
37039     {
37040         this.selectedBrick = [];
37041         
37042         this.currentSize = this.el.getBox(true);
37043         
37044         Roo.EventManager.onWindowResize(this.resize, this); 
37045
37046         if(!this.isAutoInitial){
37047             this.layout();
37048             return;
37049         }
37050         
37051         this.layout();
37052         
37053         return;
37054         //this.layout.defer(500,this);
37055         
37056     },
37057     
37058     resize : function()
37059     {
37060         var cs = this.el.getBox(true);
37061         
37062         if (
37063                 this.currentSize.width == cs.width && 
37064                 this.currentSize.x == cs.x && 
37065                 this.currentSize.height == cs.height && 
37066                 this.currentSize.y == cs.y 
37067         ) {
37068             Roo.log("no change in with or X or Y");
37069             return;
37070         }
37071         
37072         this.currentSize = cs;
37073         
37074         this.layout();
37075         
37076     },
37077     
37078     layout : function()
37079     {   
37080         this._resetLayout();
37081         
37082         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37083         
37084         this.layoutItems( isInstant );
37085       
37086         this._isLayoutInited = true;
37087         
37088         this.fireEvent('layout', this);
37089         
37090     },
37091     
37092     _resetLayout : function()
37093     {
37094         if(this.isHorizontal){
37095             this.horizontalMeasureColumns();
37096             return;
37097         }
37098         
37099         this.verticalMeasureColumns();
37100         
37101     },
37102     
37103     verticalMeasureColumns : function()
37104     {
37105         this.getContainerWidth();
37106         
37107 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37108 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37109 //            return;
37110 //        }
37111         
37112         var boxWidth = this.boxWidth + this.padWidth;
37113         
37114         if(this.containerWidth < this.boxWidth){
37115             boxWidth = this.containerWidth
37116         }
37117         
37118         var containerWidth = this.containerWidth;
37119         
37120         var cols = Math.floor(containerWidth / boxWidth);
37121         
37122         this.cols = Math.max( cols, 1 );
37123         
37124         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37125         
37126         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37127         
37128         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37129         
37130         this.colWidth = boxWidth + avail - this.padWidth;
37131         
37132         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37133         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37134     },
37135     
37136     horizontalMeasureColumns : function()
37137     {
37138         this.getContainerWidth();
37139         
37140         var boxWidth = this.boxWidth;
37141         
37142         if(this.containerWidth < boxWidth){
37143             boxWidth = this.containerWidth;
37144         }
37145         
37146         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37147         
37148         this.el.setHeight(boxWidth);
37149         
37150     },
37151     
37152     getContainerWidth : function()
37153     {
37154         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37155     },
37156     
37157     layoutItems : function( isInstant )
37158     {
37159         Roo.log(this.bricks);
37160         
37161         var items = Roo.apply([], this.bricks);
37162         
37163         if(this.isHorizontal){
37164             this._horizontalLayoutItems( items , isInstant );
37165             return;
37166         }
37167         
37168 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37169 //            this._verticalAlternativeLayoutItems( items , isInstant );
37170 //            return;
37171 //        }
37172         
37173         this._verticalLayoutItems( items , isInstant );
37174         
37175     },
37176     
37177     _verticalLayoutItems : function ( items , isInstant)
37178     {
37179         if ( !items || !items.length ) {
37180             return;
37181         }
37182         
37183         var standard = [
37184             ['xs', 'xs', 'xs', 'tall'],
37185             ['xs', 'xs', 'tall'],
37186             ['xs', 'xs', 'sm'],
37187             ['xs', 'xs', 'xs'],
37188             ['xs', 'tall'],
37189             ['xs', 'sm'],
37190             ['xs', 'xs'],
37191             ['xs'],
37192             
37193             ['sm', 'xs', 'xs'],
37194             ['sm', 'xs'],
37195             ['sm'],
37196             
37197             ['tall', 'xs', 'xs', 'xs'],
37198             ['tall', 'xs', 'xs'],
37199             ['tall', 'xs'],
37200             ['tall']
37201             
37202         ];
37203         
37204         var queue = [];
37205         
37206         var boxes = [];
37207         
37208         var box = [];
37209         
37210         Roo.each(items, function(item, k){
37211             
37212             switch (item.size) {
37213                 // these layouts take up a full box,
37214                 case 'md' :
37215                 case 'md-left' :
37216                 case 'md-right' :
37217                 case 'wide' :
37218                     
37219                     if(box.length){
37220                         boxes.push(box);
37221                         box = [];
37222                     }
37223                     
37224                     boxes.push([item]);
37225                     
37226                     break;
37227                     
37228                 case 'xs' :
37229                 case 'sm' :
37230                 case 'tall' :
37231                     
37232                     box.push(item);
37233                     
37234                     break;
37235                 default :
37236                     break;
37237                     
37238             }
37239             
37240         }, this);
37241         
37242         if(box.length){
37243             boxes.push(box);
37244             box = [];
37245         }
37246         
37247         var filterPattern = function(box, length)
37248         {
37249             if(!box.length){
37250                 return;
37251             }
37252             
37253             var match = false;
37254             
37255             var pattern = box.slice(0, length);
37256             
37257             var format = [];
37258             
37259             Roo.each(pattern, function(i){
37260                 format.push(i.size);
37261             }, this);
37262             
37263             Roo.each(standard, function(s){
37264                 
37265                 if(String(s) != String(format)){
37266                     return;
37267                 }
37268                 
37269                 match = true;
37270                 return false;
37271                 
37272             }, this);
37273             
37274             if(!match && length == 1){
37275                 return;
37276             }
37277             
37278             if(!match){
37279                 filterPattern(box, length - 1);
37280                 return;
37281             }
37282                 
37283             queue.push(pattern);
37284
37285             box = box.slice(length, box.length);
37286
37287             filterPattern(box, 4);
37288
37289             return;
37290             
37291         }
37292         
37293         Roo.each(boxes, function(box, k){
37294             
37295             if(!box.length){
37296                 return;
37297             }
37298             
37299             if(box.length == 1){
37300                 queue.push(box);
37301                 return;
37302             }
37303             
37304             filterPattern(box, 4);
37305             
37306         }, this);
37307         
37308         this._processVerticalLayoutQueue( queue, isInstant );
37309         
37310     },
37311     
37312 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37313 //    {
37314 //        if ( !items || !items.length ) {
37315 //            return;
37316 //        }
37317 //
37318 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37319 //        
37320 //    },
37321     
37322     _horizontalLayoutItems : function ( items , isInstant)
37323     {
37324         if ( !items || !items.length || items.length < 3) {
37325             return;
37326         }
37327         
37328         items.reverse();
37329         
37330         var eItems = items.slice(0, 3);
37331         
37332         items = items.slice(3, items.length);
37333         
37334         var standard = [
37335             ['xs', 'xs', 'xs', 'wide'],
37336             ['xs', 'xs', 'wide'],
37337             ['xs', 'xs', 'sm'],
37338             ['xs', 'xs', 'xs'],
37339             ['xs', 'wide'],
37340             ['xs', 'sm'],
37341             ['xs', 'xs'],
37342             ['xs'],
37343             
37344             ['sm', 'xs', 'xs'],
37345             ['sm', 'xs'],
37346             ['sm'],
37347             
37348             ['wide', 'xs', 'xs', 'xs'],
37349             ['wide', 'xs', 'xs'],
37350             ['wide', 'xs'],
37351             ['wide'],
37352             
37353             ['wide-thin']
37354         ];
37355         
37356         var queue = [];
37357         
37358         var boxes = [];
37359         
37360         var box = [];
37361         
37362         Roo.each(items, function(item, k){
37363             
37364             switch (item.size) {
37365                 case 'md' :
37366                 case 'md-left' :
37367                 case 'md-right' :
37368                 case 'tall' :
37369                     
37370                     if(box.length){
37371                         boxes.push(box);
37372                         box = [];
37373                     }
37374                     
37375                     boxes.push([item]);
37376                     
37377                     break;
37378                     
37379                 case 'xs' :
37380                 case 'sm' :
37381                 case 'wide' :
37382                 case 'wide-thin' :
37383                     
37384                     box.push(item);
37385                     
37386                     break;
37387                 default :
37388                     break;
37389                     
37390             }
37391             
37392         }, this);
37393         
37394         if(box.length){
37395             boxes.push(box);
37396             box = [];
37397         }
37398         
37399         var filterPattern = function(box, length)
37400         {
37401             if(!box.length){
37402                 return;
37403             }
37404             
37405             var match = false;
37406             
37407             var pattern = box.slice(0, length);
37408             
37409             var format = [];
37410             
37411             Roo.each(pattern, function(i){
37412                 format.push(i.size);
37413             }, this);
37414             
37415             Roo.each(standard, function(s){
37416                 
37417                 if(String(s) != String(format)){
37418                     return;
37419                 }
37420                 
37421                 match = true;
37422                 return false;
37423                 
37424             }, this);
37425             
37426             if(!match && length == 1){
37427                 return;
37428             }
37429             
37430             if(!match){
37431                 filterPattern(box, length - 1);
37432                 return;
37433             }
37434                 
37435             queue.push(pattern);
37436
37437             box = box.slice(length, box.length);
37438
37439             filterPattern(box, 4);
37440
37441             return;
37442             
37443         }
37444         
37445         Roo.each(boxes, function(box, k){
37446             
37447             if(!box.length){
37448                 return;
37449             }
37450             
37451             if(box.length == 1){
37452                 queue.push(box);
37453                 return;
37454             }
37455             
37456             filterPattern(box, 4);
37457             
37458         }, this);
37459         
37460         
37461         var prune = [];
37462         
37463         var pos = this.el.getBox(true);
37464         
37465         var minX = pos.x;
37466         
37467         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37468         
37469         var hit_end = false;
37470         
37471         Roo.each(queue, function(box){
37472             
37473             if(hit_end){
37474                 
37475                 Roo.each(box, function(b){
37476                 
37477                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37478                     b.el.hide();
37479
37480                 }, this);
37481
37482                 return;
37483             }
37484             
37485             var mx = 0;
37486             
37487             Roo.each(box, function(b){
37488                 
37489                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37490                 b.el.show();
37491
37492                 mx = Math.max(mx, b.x);
37493                 
37494             }, this);
37495             
37496             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37497             
37498             if(maxX < minX){
37499                 
37500                 Roo.each(box, function(b){
37501                 
37502                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37503                     b.el.hide();
37504                     
37505                 }, this);
37506                 
37507                 hit_end = true;
37508                 
37509                 return;
37510             }
37511             
37512             prune.push(box);
37513             
37514         }, this);
37515         
37516         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37517     },
37518     
37519     /** Sets position of item in DOM
37520     * @param {Element} item
37521     * @param {Number} x - horizontal position
37522     * @param {Number} y - vertical position
37523     * @param {Boolean} isInstant - disables transitions
37524     */
37525     _processVerticalLayoutQueue : function( queue, isInstant )
37526     {
37527         var pos = this.el.getBox(true);
37528         var x = pos.x;
37529         var y = pos.y;
37530         var maxY = [];
37531         
37532         for (var i = 0; i < this.cols; i++){
37533             maxY[i] = pos.y;
37534         }
37535         
37536         Roo.each(queue, function(box, k){
37537             
37538             var col = k % this.cols;
37539             
37540             Roo.each(box, function(b,kk){
37541                 
37542                 b.el.position('absolute');
37543                 
37544                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37545                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37546                 
37547                 if(b.size == 'md-left' || b.size == 'md-right'){
37548                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37549                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37550                 }
37551                 
37552                 b.el.setWidth(width);
37553                 b.el.setHeight(height);
37554                 // iframe?
37555                 b.el.select('iframe',true).setSize(width,height);
37556                 
37557             }, this);
37558             
37559             for (var i = 0; i < this.cols; i++){
37560                 
37561                 if(maxY[i] < maxY[col]){
37562                     col = i;
37563                     continue;
37564                 }
37565                 
37566                 col = Math.min(col, i);
37567                 
37568             }
37569             
37570             x = pos.x + col * (this.colWidth + this.padWidth);
37571             
37572             y = maxY[col];
37573             
37574             var positions = [];
37575             
37576             switch (box.length){
37577                 case 1 :
37578                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37579                     break;
37580                 case 2 :
37581                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37582                     break;
37583                 case 3 :
37584                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37585                     break;
37586                 case 4 :
37587                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37588                     break;
37589                 default :
37590                     break;
37591             }
37592             
37593             Roo.each(box, function(b,kk){
37594                 
37595                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37596                 
37597                 var sz = b.el.getSize();
37598                 
37599                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37600                 
37601             }, this);
37602             
37603         }, this);
37604         
37605         var mY = 0;
37606         
37607         for (var i = 0; i < this.cols; i++){
37608             mY = Math.max(mY, maxY[i]);
37609         }
37610         
37611         this.el.setHeight(mY - pos.y);
37612         
37613     },
37614     
37615 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37616 //    {
37617 //        var pos = this.el.getBox(true);
37618 //        var x = pos.x;
37619 //        var y = pos.y;
37620 //        var maxX = pos.right;
37621 //        
37622 //        var maxHeight = 0;
37623 //        
37624 //        Roo.each(items, function(item, k){
37625 //            
37626 //            var c = k % 2;
37627 //            
37628 //            item.el.position('absolute');
37629 //                
37630 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37631 //
37632 //            item.el.setWidth(width);
37633 //
37634 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37635 //
37636 //            item.el.setHeight(height);
37637 //            
37638 //            if(c == 0){
37639 //                item.el.setXY([x, y], isInstant ? false : true);
37640 //            } else {
37641 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37642 //            }
37643 //            
37644 //            y = y + height + this.alternativePadWidth;
37645 //            
37646 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37647 //            
37648 //        }, this);
37649 //        
37650 //        this.el.setHeight(maxHeight);
37651 //        
37652 //    },
37653     
37654     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37655     {
37656         var pos = this.el.getBox(true);
37657         
37658         var minX = pos.x;
37659         var minY = pos.y;
37660         
37661         var maxX = pos.right;
37662         
37663         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37664         
37665         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37666         
37667         Roo.each(queue, function(box, k){
37668             
37669             Roo.each(box, function(b, kk){
37670                 
37671                 b.el.position('absolute');
37672                 
37673                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37674                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37675                 
37676                 if(b.size == 'md-left' || b.size == 'md-right'){
37677                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37678                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37679                 }
37680                 
37681                 b.el.setWidth(width);
37682                 b.el.setHeight(height);
37683                 
37684             }, this);
37685             
37686             if(!box.length){
37687                 return;
37688             }
37689             
37690             var positions = [];
37691             
37692             switch (box.length){
37693                 case 1 :
37694                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37695                     break;
37696                 case 2 :
37697                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37698                     break;
37699                 case 3 :
37700                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37701                     break;
37702                 case 4 :
37703                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37704                     break;
37705                 default :
37706                     break;
37707             }
37708             
37709             Roo.each(box, function(b,kk){
37710                 
37711                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37712                 
37713                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37714                 
37715             }, this);
37716             
37717         }, this);
37718         
37719     },
37720     
37721     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37722     {
37723         Roo.each(eItems, function(b,k){
37724             
37725             b.size = (k == 0) ? 'sm' : 'xs';
37726             b.x = (k == 0) ? 2 : 1;
37727             b.y = (k == 0) ? 2 : 1;
37728             
37729             b.el.position('absolute');
37730             
37731             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37732                 
37733             b.el.setWidth(width);
37734             
37735             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37736             
37737             b.el.setHeight(height);
37738             
37739         }, this);
37740
37741         var positions = [];
37742         
37743         positions.push({
37744             x : maxX - this.unitWidth * 2 - this.gutter,
37745             y : minY
37746         });
37747         
37748         positions.push({
37749             x : maxX - this.unitWidth,
37750             y : minY + (this.unitWidth + this.gutter) * 2
37751         });
37752         
37753         positions.push({
37754             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37755             y : minY
37756         });
37757         
37758         Roo.each(eItems, function(b,k){
37759             
37760             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37761
37762         }, this);
37763         
37764     },
37765     
37766     getVerticalOneBoxColPositions : function(x, y, box)
37767     {
37768         var pos = [];
37769         
37770         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37771         
37772         if(box[0].size == 'md-left'){
37773             rand = 0;
37774         }
37775         
37776         if(box[0].size == 'md-right'){
37777             rand = 1;
37778         }
37779         
37780         pos.push({
37781             x : x + (this.unitWidth + this.gutter) * rand,
37782             y : y
37783         });
37784         
37785         return pos;
37786     },
37787     
37788     getVerticalTwoBoxColPositions : function(x, y, box)
37789     {
37790         var pos = [];
37791         
37792         if(box[0].size == 'xs'){
37793             
37794             pos.push({
37795                 x : x,
37796                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37797             });
37798
37799             pos.push({
37800                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37801                 y : y
37802             });
37803             
37804             return pos;
37805             
37806         }
37807         
37808         pos.push({
37809             x : x,
37810             y : y
37811         });
37812
37813         pos.push({
37814             x : x + (this.unitWidth + this.gutter) * 2,
37815             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37816         });
37817         
37818         return pos;
37819         
37820     },
37821     
37822     getVerticalThreeBoxColPositions : function(x, y, box)
37823     {
37824         var pos = [];
37825         
37826         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37827             
37828             pos.push({
37829                 x : x,
37830                 y : y
37831             });
37832
37833             pos.push({
37834                 x : x + (this.unitWidth + this.gutter) * 1,
37835                 y : y
37836             });
37837             
37838             pos.push({
37839                 x : x + (this.unitWidth + this.gutter) * 2,
37840                 y : y
37841             });
37842             
37843             return pos;
37844             
37845         }
37846         
37847         if(box[0].size == 'xs' && box[1].size == 'xs'){
37848             
37849             pos.push({
37850                 x : x,
37851                 y : y
37852             });
37853
37854             pos.push({
37855                 x : x,
37856                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37857             });
37858             
37859             pos.push({
37860                 x : x + (this.unitWidth + this.gutter) * 1,
37861                 y : y
37862             });
37863             
37864             return pos;
37865             
37866         }
37867         
37868         pos.push({
37869             x : x,
37870             y : y
37871         });
37872
37873         pos.push({
37874             x : x + (this.unitWidth + this.gutter) * 2,
37875             y : y
37876         });
37877
37878         pos.push({
37879             x : x + (this.unitWidth + this.gutter) * 2,
37880             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37881         });
37882             
37883         return pos;
37884         
37885     },
37886     
37887     getVerticalFourBoxColPositions : function(x, y, box)
37888     {
37889         var pos = [];
37890         
37891         if(box[0].size == 'xs'){
37892             
37893             pos.push({
37894                 x : x,
37895                 y : y
37896             });
37897
37898             pos.push({
37899                 x : x,
37900                 y : y + (this.unitHeight + this.gutter) * 1
37901             });
37902             
37903             pos.push({
37904                 x : x,
37905                 y : y + (this.unitHeight + this.gutter) * 2
37906             });
37907             
37908             pos.push({
37909                 x : x + (this.unitWidth + this.gutter) * 1,
37910                 y : y
37911             });
37912             
37913             return pos;
37914             
37915         }
37916         
37917         pos.push({
37918             x : x,
37919             y : y
37920         });
37921
37922         pos.push({
37923             x : x + (this.unitWidth + this.gutter) * 2,
37924             y : y
37925         });
37926
37927         pos.push({
37928             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37929             y : y + (this.unitHeight + this.gutter) * 1
37930         });
37931
37932         pos.push({
37933             x : x + (this.unitWidth + this.gutter) * 2,
37934             y : y + (this.unitWidth + this.gutter) * 2
37935         });
37936
37937         return pos;
37938         
37939     },
37940     
37941     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37942     {
37943         var pos = [];
37944         
37945         if(box[0].size == 'md-left'){
37946             pos.push({
37947                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37948                 y : minY
37949             });
37950             
37951             return pos;
37952         }
37953         
37954         if(box[0].size == 'md-right'){
37955             pos.push({
37956                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37957                 y : minY + (this.unitWidth + this.gutter) * 1
37958             });
37959             
37960             return pos;
37961         }
37962         
37963         var rand = Math.floor(Math.random() * (4 - box[0].y));
37964         
37965         pos.push({
37966             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37967             y : minY + (this.unitWidth + this.gutter) * rand
37968         });
37969         
37970         return pos;
37971         
37972     },
37973     
37974     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37975     {
37976         var pos = [];
37977         
37978         if(box[0].size == 'xs'){
37979             
37980             pos.push({
37981                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37982                 y : minY
37983             });
37984
37985             pos.push({
37986                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37987                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37988             });
37989             
37990             return pos;
37991             
37992         }
37993         
37994         pos.push({
37995             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37996             y : minY
37997         });
37998
37999         pos.push({
38000             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38001             y : minY + (this.unitWidth + this.gutter) * 2
38002         });
38003         
38004         return pos;
38005         
38006     },
38007     
38008     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38009     {
38010         var pos = [];
38011         
38012         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38013             
38014             pos.push({
38015                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38016                 y : minY
38017             });
38018
38019             pos.push({
38020                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38021                 y : minY + (this.unitWidth + this.gutter) * 1
38022             });
38023             
38024             pos.push({
38025                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38026                 y : minY + (this.unitWidth + this.gutter) * 2
38027             });
38028             
38029             return pos;
38030             
38031         }
38032         
38033         if(box[0].size == 'xs' && box[1].size == 'xs'){
38034             
38035             pos.push({
38036                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38037                 y : minY
38038             });
38039
38040             pos.push({
38041                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38042                 y : minY
38043             });
38044             
38045             pos.push({
38046                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38047                 y : minY + (this.unitWidth + this.gutter) * 1
38048             });
38049             
38050             return pos;
38051             
38052         }
38053         
38054         pos.push({
38055             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38056             y : minY
38057         });
38058
38059         pos.push({
38060             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38061             y : minY + (this.unitWidth + this.gutter) * 2
38062         });
38063
38064         pos.push({
38065             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38066             y : minY + (this.unitWidth + this.gutter) * 2
38067         });
38068             
38069         return pos;
38070         
38071     },
38072     
38073     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38074     {
38075         var pos = [];
38076         
38077         if(box[0].size == 'xs'){
38078             
38079             pos.push({
38080                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38081                 y : minY
38082             });
38083
38084             pos.push({
38085                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38086                 y : minY
38087             });
38088             
38089             pos.push({
38090                 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),
38091                 y : minY
38092             });
38093             
38094             pos.push({
38095                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38096                 y : minY + (this.unitWidth + this.gutter) * 1
38097             });
38098             
38099             return pos;
38100             
38101         }
38102         
38103         pos.push({
38104             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38105             y : minY
38106         });
38107         
38108         pos.push({
38109             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38110             y : minY + (this.unitWidth + this.gutter) * 2
38111         });
38112         
38113         pos.push({
38114             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38115             y : minY + (this.unitWidth + this.gutter) * 2
38116         });
38117         
38118         pos.push({
38119             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),
38120             y : minY + (this.unitWidth + this.gutter) * 2
38121         });
38122
38123         return pos;
38124         
38125     },
38126     
38127     /**
38128     * remove a Masonry Brick
38129     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38130     */
38131     removeBrick : function(brick_id)
38132     {
38133         if (!brick_id) {
38134             return;
38135         }
38136         
38137         for (var i = 0; i<this.bricks.length; i++) {
38138             if (this.bricks[i].id == brick_id) {
38139                 this.bricks.splice(i,1);
38140                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38141                 this.initial();
38142             }
38143         }
38144     },
38145     
38146     /**
38147     * adds a Masonry Brick
38148     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38149     */
38150     addBrick : function(cfg)
38151     {
38152         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38153         //this.register(cn);
38154         cn.parentId = this.id;
38155         cn.render(this.el);
38156         return cn;
38157     },
38158     
38159     /**
38160     * register a Masonry Brick
38161     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38162     */
38163     
38164     register : function(brick)
38165     {
38166         this.bricks.push(brick);
38167         brick.masonryId = this.id;
38168     },
38169     
38170     /**
38171     * clear all the Masonry Brick
38172     */
38173     clearAll : function()
38174     {
38175         this.bricks = [];
38176         //this.getChildContainer().dom.innerHTML = "";
38177         this.el.dom.innerHTML = '';
38178     },
38179     
38180     getSelected : function()
38181     {
38182         if (!this.selectedBrick) {
38183             return false;
38184         }
38185         
38186         return this.selectedBrick;
38187     }
38188 });
38189
38190 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38191     
38192     groups: {},
38193      /**
38194     * register a Masonry Layout
38195     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38196     */
38197     
38198     register : function(layout)
38199     {
38200         this.groups[layout.id] = layout;
38201     },
38202     /**
38203     * fetch a  Masonry Layout based on the masonry layout ID
38204     * @param {string} the masonry layout to add
38205     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38206     */
38207     
38208     get: function(layout_id) {
38209         if (typeof(this.groups[layout_id]) == 'undefined') {
38210             return false;
38211         }
38212         return this.groups[layout_id] ;
38213     }
38214     
38215     
38216     
38217 });
38218
38219  
38220
38221  /**
38222  *
38223  * This is based on 
38224  * http://masonry.desandro.com
38225  *
38226  * The idea is to render all the bricks based on vertical width...
38227  *
38228  * The original code extends 'outlayer' - we might need to use that....
38229  * 
38230  */
38231
38232
38233 /**
38234  * @class Roo.bootstrap.LayoutMasonryAuto
38235  * @extends Roo.bootstrap.Component
38236  * Bootstrap Layout Masonry class
38237  * 
38238  * @constructor
38239  * Create a new Element
38240  * @param {Object} config The config object
38241  */
38242
38243 Roo.bootstrap.LayoutMasonryAuto = function(config){
38244     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38245 };
38246
38247 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38248     
38249       /**
38250      * @cfg {Boolean} isFitWidth  - resize the width..
38251      */   
38252     isFitWidth : false,  // options..
38253     /**
38254      * @cfg {Boolean} isOriginLeft = left align?
38255      */   
38256     isOriginLeft : true,
38257     /**
38258      * @cfg {Boolean} isOriginTop = top align?
38259      */   
38260     isOriginTop : false,
38261     /**
38262      * @cfg {Boolean} isLayoutInstant = no animation?
38263      */   
38264     isLayoutInstant : false, // needed?
38265     /**
38266      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38267      */   
38268     isResizingContainer : true,
38269     /**
38270      * @cfg {Number} columnWidth  width of the columns 
38271      */   
38272     
38273     columnWidth : 0,
38274     
38275     /**
38276      * @cfg {Number} maxCols maximum number of columns
38277      */   
38278     
38279     maxCols: 0,
38280     /**
38281      * @cfg {Number} padHeight padding below box..
38282      */   
38283     
38284     padHeight : 10, 
38285     
38286     /**
38287      * @cfg {Boolean} isAutoInitial defalut true
38288      */   
38289     
38290     isAutoInitial : true, 
38291     
38292     // private?
38293     gutter : 0,
38294     
38295     containerWidth: 0,
38296     initialColumnWidth : 0,
38297     currentSize : null,
38298     
38299     colYs : null, // array.
38300     maxY : 0,
38301     padWidth: 10,
38302     
38303     
38304     tag: 'div',
38305     cls: '',
38306     bricks: null, //CompositeElement
38307     cols : 0, // array?
38308     // element : null, // wrapped now this.el
38309     _isLayoutInited : null, 
38310     
38311     
38312     getAutoCreate : function(){
38313         
38314         var cfg = {
38315             tag: this.tag,
38316             cls: 'blog-masonary-wrapper ' + this.cls,
38317             cn : {
38318                 cls : 'mas-boxes masonary'
38319             }
38320         };
38321         
38322         return cfg;
38323     },
38324     
38325     getChildContainer: function( )
38326     {
38327         if (this.boxesEl) {
38328             return this.boxesEl;
38329         }
38330         
38331         this.boxesEl = this.el.select('.mas-boxes').first();
38332         
38333         return this.boxesEl;
38334     },
38335     
38336     
38337     initEvents : function()
38338     {
38339         var _this = this;
38340         
38341         if(this.isAutoInitial){
38342             Roo.log('hook children rendered');
38343             this.on('childrenrendered', function() {
38344                 Roo.log('children rendered');
38345                 _this.initial();
38346             } ,this);
38347         }
38348         
38349     },
38350     
38351     initial : function()
38352     {
38353         this.reloadItems();
38354
38355         this.currentSize = this.el.getBox(true);
38356
38357         /// was window resize... - let's see if this works..
38358         Roo.EventManager.onWindowResize(this.resize, this); 
38359
38360         if(!this.isAutoInitial){
38361             this.layout();
38362             return;
38363         }
38364         
38365         this.layout.defer(500,this);
38366     },
38367     
38368     reloadItems: function()
38369     {
38370         this.bricks = this.el.select('.masonry-brick', true);
38371         
38372         this.bricks.each(function(b) {
38373             //Roo.log(b.getSize());
38374             if (!b.attr('originalwidth')) {
38375                 b.attr('originalwidth',  b.getSize().width);
38376             }
38377             
38378         });
38379         
38380         Roo.log(this.bricks.elements.length);
38381     },
38382     
38383     resize : function()
38384     {
38385         Roo.log('resize');
38386         var cs = this.el.getBox(true);
38387         
38388         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38389             Roo.log("no change in with or X");
38390             return;
38391         }
38392         this.currentSize = cs;
38393         this.layout();
38394     },
38395     
38396     layout : function()
38397     {
38398          Roo.log('layout');
38399         this._resetLayout();
38400         //this._manageStamps();
38401       
38402         // don't animate first layout
38403         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38404         this.layoutItems( isInstant );
38405       
38406         // flag for initalized
38407         this._isLayoutInited = true;
38408     },
38409     
38410     layoutItems : function( isInstant )
38411     {
38412         //var items = this._getItemsForLayout( this.items );
38413         // original code supports filtering layout items.. we just ignore it..
38414         
38415         this._layoutItems( this.bricks , isInstant );
38416       
38417         this._postLayout();
38418     },
38419     _layoutItems : function ( items , isInstant)
38420     {
38421        //this.fireEvent( 'layout', this, items );
38422     
38423
38424         if ( !items || !items.elements.length ) {
38425           // no items, emit event with empty array
38426             return;
38427         }
38428
38429         var queue = [];
38430         items.each(function(item) {
38431             Roo.log("layout item");
38432             Roo.log(item);
38433             // get x/y object from method
38434             var position = this._getItemLayoutPosition( item );
38435             // enqueue
38436             position.item = item;
38437             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38438             queue.push( position );
38439         }, this);
38440       
38441         this._processLayoutQueue( queue );
38442     },
38443     /** Sets position of item in DOM
38444     * @param {Element} item
38445     * @param {Number} x - horizontal position
38446     * @param {Number} y - vertical position
38447     * @param {Boolean} isInstant - disables transitions
38448     */
38449     _processLayoutQueue : function( queue )
38450     {
38451         for ( var i=0, len = queue.length; i < len; i++ ) {
38452             var obj = queue[i];
38453             obj.item.position('absolute');
38454             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38455         }
38456     },
38457       
38458     
38459     /**
38460     * Any logic you want to do after each layout,
38461     * i.e. size the container
38462     */
38463     _postLayout : function()
38464     {
38465         this.resizeContainer();
38466     },
38467     
38468     resizeContainer : function()
38469     {
38470         if ( !this.isResizingContainer ) {
38471             return;
38472         }
38473         var size = this._getContainerSize();
38474         if ( size ) {
38475             this.el.setSize(size.width,size.height);
38476             this.boxesEl.setSize(size.width,size.height);
38477         }
38478     },
38479     
38480     
38481     
38482     _resetLayout : function()
38483     {
38484         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38485         this.colWidth = this.el.getWidth();
38486         //this.gutter = this.el.getWidth(); 
38487         
38488         this.measureColumns();
38489
38490         // reset column Y
38491         var i = this.cols;
38492         this.colYs = [];
38493         while (i--) {
38494             this.colYs.push( 0 );
38495         }
38496     
38497         this.maxY = 0;
38498     },
38499
38500     measureColumns : function()
38501     {
38502         this.getContainerWidth();
38503       // if columnWidth is 0, default to outerWidth of first item
38504         if ( !this.columnWidth ) {
38505             var firstItem = this.bricks.first();
38506             Roo.log(firstItem);
38507             this.columnWidth  = this.containerWidth;
38508             if (firstItem && firstItem.attr('originalwidth') ) {
38509                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38510             }
38511             // columnWidth fall back to item of first element
38512             Roo.log("set column width?");
38513                         this.initialColumnWidth = this.columnWidth  ;
38514
38515             // if first elem has no width, default to size of container
38516             
38517         }
38518         
38519         
38520         if (this.initialColumnWidth) {
38521             this.columnWidth = this.initialColumnWidth;
38522         }
38523         
38524         
38525             
38526         // column width is fixed at the top - however if container width get's smaller we should
38527         // reduce it...
38528         
38529         // this bit calcs how man columns..
38530             
38531         var columnWidth = this.columnWidth += this.gutter;
38532       
38533         // calculate columns
38534         var containerWidth = this.containerWidth + this.gutter;
38535         
38536         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38537         // fix rounding errors, typically with gutters
38538         var excess = columnWidth - containerWidth % columnWidth;
38539         
38540         
38541         // if overshoot is less than a pixel, round up, otherwise floor it
38542         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38543         cols = Math[ mathMethod ]( cols );
38544         this.cols = Math.max( cols, 1 );
38545         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38546         
38547          // padding positioning..
38548         var totalColWidth = this.cols * this.columnWidth;
38549         var padavail = this.containerWidth - totalColWidth;
38550         // so for 2 columns - we need 3 'pads'
38551         
38552         var padNeeded = (1+this.cols) * this.padWidth;
38553         
38554         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38555         
38556         this.columnWidth += padExtra
38557         //this.padWidth = Math.floor(padavail /  ( this.cols));
38558         
38559         // adjust colum width so that padding is fixed??
38560         
38561         // we have 3 columns ... total = width * 3
38562         // we have X left over... that should be used by 
38563         
38564         //if (this.expandC) {
38565             
38566         //}
38567         
38568         
38569         
38570     },
38571     
38572     getContainerWidth : function()
38573     {
38574        /* // container is parent if fit width
38575         var container = this.isFitWidth ? this.element.parentNode : this.element;
38576         // check that this.size and size are there
38577         // IE8 triggers resize on body size change, so they might not be
38578         
38579         var size = getSize( container );  //FIXME
38580         this.containerWidth = size && size.innerWidth; //FIXME
38581         */
38582          
38583         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38584         
38585     },
38586     
38587     _getItemLayoutPosition : function( item )  // what is item?
38588     {
38589         // we resize the item to our columnWidth..
38590       
38591         item.setWidth(this.columnWidth);
38592         item.autoBoxAdjust  = false;
38593         
38594         var sz = item.getSize();
38595  
38596         // how many columns does this brick span
38597         var remainder = this.containerWidth % this.columnWidth;
38598         
38599         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38600         // round if off by 1 pixel, otherwise use ceil
38601         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38602         colSpan = Math.min( colSpan, this.cols );
38603         
38604         // normally this should be '1' as we dont' currently allow multi width columns..
38605         
38606         var colGroup = this._getColGroup( colSpan );
38607         // get the minimum Y value from the columns
38608         var minimumY = Math.min.apply( Math, colGroup );
38609         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38610         
38611         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38612          
38613         // position the brick
38614         var position = {
38615             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38616             y: this.currentSize.y + minimumY + this.padHeight
38617         };
38618         
38619         Roo.log(position);
38620         // apply setHeight to necessary columns
38621         var setHeight = minimumY + sz.height + this.padHeight;
38622         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38623         
38624         var setSpan = this.cols + 1 - colGroup.length;
38625         for ( var i = 0; i < setSpan; i++ ) {
38626           this.colYs[ shortColIndex + i ] = setHeight ;
38627         }
38628       
38629         return position;
38630     },
38631     
38632     /**
38633      * @param {Number} colSpan - number of columns the element spans
38634      * @returns {Array} colGroup
38635      */
38636     _getColGroup : function( colSpan )
38637     {
38638         if ( colSpan < 2 ) {
38639           // if brick spans only one column, use all the column Ys
38640           return this.colYs;
38641         }
38642       
38643         var colGroup = [];
38644         // how many different places could this brick fit horizontally
38645         var groupCount = this.cols + 1 - colSpan;
38646         // for each group potential horizontal position
38647         for ( var i = 0; i < groupCount; i++ ) {
38648           // make an array of colY values for that one group
38649           var groupColYs = this.colYs.slice( i, i + colSpan );
38650           // and get the max value of the array
38651           colGroup[i] = Math.max.apply( Math, groupColYs );
38652         }
38653         return colGroup;
38654     },
38655     /*
38656     _manageStamp : function( stamp )
38657     {
38658         var stampSize =  stamp.getSize();
38659         var offset = stamp.getBox();
38660         // get the columns that this stamp affects
38661         var firstX = this.isOriginLeft ? offset.x : offset.right;
38662         var lastX = firstX + stampSize.width;
38663         var firstCol = Math.floor( firstX / this.columnWidth );
38664         firstCol = Math.max( 0, firstCol );
38665         
38666         var lastCol = Math.floor( lastX / this.columnWidth );
38667         // lastCol should not go over if multiple of columnWidth #425
38668         lastCol -= lastX % this.columnWidth ? 0 : 1;
38669         lastCol = Math.min( this.cols - 1, lastCol );
38670         
38671         // set colYs to bottom of the stamp
38672         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38673             stampSize.height;
38674             
38675         for ( var i = firstCol; i <= lastCol; i++ ) {
38676           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38677         }
38678     },
38679     */
38680     
38681     _getContainerSize : function()
38682     {
38683         this.maxY = Math.max.apply( Math, this.colYs );
38684         var size = {
38685             height: this.maxY
38686         };
38687       
38688         if ( this.isFitWidth ) {
38689             size.width = this._getContainerFitWidth();
38690         }
38691       
38692         return size;
38693     },
38694     
38695     _getContainerFitWidth : function()
38696     {
38697         var unusedCols = 0;
38698         // count unused columns
38699         var i = this.cols;
38700         while ( --i ) {
38701           if ( this.colYs[i] !== 0 ) {
38702             break;
38703           }
38704           unusedCols++;
38705         }
38706         // fit container to columns that have been used
38707         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38708     },
38709     
38710     needsResizeLayout : function()
38711     {
38712         var previousWidth = this.containerWidth;
38713         this.getContainerWidth();
38714         return previousWidth !== this.containerWidth;
38715     }
38716  
38717 });
38718
38719  
38720
38721  /*
38722  * - LGPL
38723  *
38724  * element
38725  * 
38726  */
38727
38728 /**
38729  * @class Roo.bootstrap.MasonryBrick
38730  * @extends Roo.bootstrap.Component
38731  * Bootstrap MasonryBrick class
38732  * 
38733  * @constructor
38734  * Create a new MasonryBrick
38735  * @param {Object} config The config object
38736  */
38737
38738 Roo.bootstrap.MasonryBrick = function(config){
38739     
38740     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38741     
38742     Roo.bootstrap.MasonryBrick.register(this);
38743     
38744     this.addEvents({
38745         // raw events
38746         /**
38747          * @event click
38748          * When a MasonryBrick is clcik
38749          * @param {Roo.bootstrap.MasonryBrick} this
38750          * @param {Roo.EventObject} e
38751          */
38752         "click" : true
38753     });
38754 };
38755
38756 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38757     
38758     /**
38759      * @cfg {String} title
38760      */   
38761     title : '',
38762     /**
38763      * @cfg {String} html
38764      */   
38765     html : '',
38766     /**
38767      * @cfg {String} bgimage
38768      */   
38769     bgimage : '',
38770     /**
38771      * @cfg {String} videourl
38772      */   
38773     videourl : '',
38774     /**
38775      * @cfg {String} cls
38776      */   
38777     cls : '',
38778     /**
38779      * @cfg {String} href
38780      */   
38781     href : '',
38782     /**
38783      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38784      */   
38785     size : 'xs',
38786     
38787     /**
38788      * @cfg {String} placetitle (center|bottom)
38789      */   
38790     placetitle : '',
38791     
38792     /**
38793      * @cfg {Boolean} isFitContainer defalut true
38794      */   
38795     isFitContainer : true, 
38796     
38797     /**
38798      * @cfg {Boolean} preventDefault defalut false
38799      */   
38800     preventDefault : false, 
38801     
38802     /**
38803      * @cfg {Boolean} inverse defalut false
38804      */   
38805     maskInverse : false, 
38806     
38807     getAutoCreate : function()
38808     {
38809         if(!this.isFitContainer){
38810             return this.getSplitAutoCreate();
38811         }
38812         
38813         var cls = 'masonry-brick masonry-brick-full';
38814         
38815         if(this.href.length){
38816             cls += ' masonry-brick-link';
38817         }
38818         
38819         if(this.bgimage.length){
38820             cls += ' masonry-brick-image';
38821         }
38822         
38823         if(this.maskInverse){
38824             cls += ' mask-inverse';
38825         }
38826         
38827         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38828             cls += ' enable-mask';
38829         }
38830         
38831         if(this.size){
38832             cls += ' masonry-' + this.size + '-brick';
38833         }
38834         
38835         if(this.placetitle.length){
38836             
38837             switch (this.placetitle) {
38838                 case 'center' :
38839                     cls += ' masonry-center-title';
38840                     break;
38841                 case 'bottom' :
38842                     cls += ' masonry-bottom-title';
38843                     break;
38844                 default:
38845                     break;
38846             }
38847             
38848         } else {
38849             if(!this.html.length && !this.bgimage.length){
38850                 cls += ' masonry-center-title';
38851             }
38852
38853             if(!this.html.length && this.bgimage.length){
38854                 cls += ' masonry-bottom-title';
38855             }
38856         }
38857         
38858         if(this.cls){
38859             cls += ' ' + this.cls;
38860         }
38861         
38862         var cfg = {
38863             tag: (this.href.length) ? 'a' : 'div',
38864             cls: cls,
38865             cn: [
38866                 {
38867                     tag: 'div',
38868                     cls: 'masonry-brick-mask'
38869                 },
38870                 {
38871                     tag: 'div',
38872                     cls: 'masonry-brick-paragraph',
38873                     cn: []
38874                 }
38875             ]
38876         };
38877         
38878         if(this.href.length){
38879             cfg.href = this.href;
38880         }
38881         
38882         var cn = cfg.cn[1].cn;
38883         
38884         if(this.title.length){
38885             cn.push({
38886                 tag: 'h4',
38887                 cls: 'masonry-brick-title',
38888                 html: this.title
38889             });
38890         }
38891         
38892         if(this.html.length){
38893             cn.push({
38894                 tag: 'p',
38895                 cls: 'masonry-brick-text',
38896                 html: this.html
38897             });
38898         }
38899         
38900         if (!this.title.length && !this.html.length) {
38901             cfg.cn[1].cls += ' hide';
38902         }
38903         
38904         if(this.bgimage.length){
38905             cfg.cn.push({
38906                 tag: 'img',
38907                 cls: 'masonry-brick-image-view',
38908                 src: this.bgimage
38909             });
38910         }
38911         
38912         if(this.videourl.length){
38913             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38914             // youtube support only?
38915             cfg.cn.push({
38916                 tag: 'iframe',
38917                 cls: 'masonry-brick-image-view',
38918                 src: vurl,
38919                 frameborder : 0,
38920                 allowfullscreen : true
38921             });
38922         }
38923         
38924         return cfg;
38925         
38926     },
38927     
38928     getSplitAutoCreate : function()
38929     {
38930         var cls = 'masonry-brick masonry-brick-split';
38931         
38932         if(this.href.length){
38933             cls += ' masonry-brick-link';
38934         }
38935         
38936         if(this.bgimage.length){
38937             cls += ' masonry-brick-image';
38938         }
38939         
38940         if(this.size){
38941             cls += ' masonry-' + this.size + '-brick';
38942         }
38943         
38944         switch (this.placetitle) {
38945             case 'center' :
38946                 cls += ' masonry-center-title';
38947                 break;
38948             case 'bottom' :
38949                 cls += ' masonry-bottom-title';
38950                 break;
38951             default:
38952                 if(!this.bgimage.length){
38953                     cls += ' masonry-center-title';
38954                 }
38955
38956                 if(this.bgimage.length){
38957                     cls += ' masonry-bottom-title';
38958                 }
38959                 break;
38960         }
38961         
38962         if(this.cls){
38963             cls += ' ' + this.cls;
38964         }
38965         
38966         var cfg = {
38967             tag: (this.href.length) ? 'a' : 'div',
38968             cls: cls,
38969             cn: [
38970                 {
38971                     tag: 'div',
38972                     cls: 'masonry-brick-split-head',
38973                     cn: [
38974                         {
38975                             tag: 'div',
38976                             cls: 'masonry-brick-paragraph',
38977                             cn: []
38978                         }
38979                     ]
38980                 },
38981                 {
38982                     tag: 'div',
38983                     cls: 'masonry-brick-split-body',
38984                     cn: []
38985                 }
38986             ]
38987         };
38988         
38989         if(this.href.length){
38990             cfg.href = this.href;
38991         }
38992         
38993         if(this.title.length){
38994             cfg.cn[0].cn[0].cn.push({
38995                 tag: 'h4',
38996                 cls: 'masonry-brick-title',
38997                 html: this.title
38998             });
38999         }
39000         
39001         if(this.html.length){
39002             cfg.cn[1].cn.push({
39003                 tag: 'p',
39004                 cls: 'masonry-brick-text',
39005                 html: this.html
39006             });
39007         }
39008
39009         if(this.bgimage.length){
39010             cfg.cn[0].cn.push({
39011                 tag: 'img',
39012                 cls: 'masonry-brick-image-view',
39013                 src: this.bgimage
39014             });
39015         }
39016         
39017         if(this.videourl.length){
39018             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39019             // youtube support only?
39020             cfg.cn[0].cn.cn.push({
39021                 tag: 'iframe',
39022                 cls: 'masonry-brick-image-view',
39023                 src: vurl,
39024                 frameborder : 0,
39025                 allowfullscreen : true
39026             });
39027         }
39028         
39029         return cfg;
39030     },
39031     
39032     initEvents: function() 
39033     {
39034         switch (this.size) {
39035             case 'xs' :
39036                 this.x = 1;
39037                 this.y = 1;
39038                 break;
39039             case 'sm' :
39040                 this.x = 2;
39041                 this.y = 2;
39042                 break;
39043             case 'md' :
39044             case 'md-left' :
39045             case 'md-right' :
39046                 this.x = 3;
39047                 this.y = 3;
39048                 break;
39049             case 'tall' :
39050                 this.x = 2;
39051                 this.y = 3;
39052                 break;
39053             case 'wide' :
39054                 this.x = 3;
39055                 this.y = 2;
39056                 break;
39057             case 'wide-thin' :
39058                 this.x = 3;
39059                 this.y = 1;
39060                 break;
39061                         
39062             default :
39063                 break;
39064         }
39065         
39066         if(Roo.isTouch){
39067             this.el.on('touchstart', this.onTouchStart, this);
39068             this.el.on('touchmove', this.onTouchMove, this);
39069             this.el.on('touchend', this.onTouchEnd, this);
39070             this.el.on('contextmenu', this.onContextMenu, this);
39071         } else {
39072             this.el.on('mouseenter'  ,this.enter, this);
39073             this.el.on('mouseleave', this.leave, this);
39074             this.el.on('click', this.onClick, this);
39075         }
39076         
39077         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39078             this.parent().bricks.push(this);   
39079         }
39080         
39081     },
39082     
39083     onClick: function(e, el)
39084     {
39085         var time = this.endTimer - this.startTimer;
39086         // Roo.log(e.preventDefault());
39087         if(Roo.isTouch){
39088             if(time > 1000){
39089                 e.preventDefault();
39090                 return;
39091             }
39092         }
39093         
39094         if(!this.preventDefault){
39095             return;
39096         }
39097         
39098         e.preventDefault();
39099         
39100         if (this.activeClass != '') {
39101             this.selectBrick();
39102         }
39103         
39104         this.fireEvent('click', this, e);
39105     },
39106     
39107     enter: function(e, el)
39108     {
39109         e.preventDefault();
39110         
39111         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39112             return;
39113         }
39114         
39115         if(this.bgimage.length && this.html.length){
39116             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39117         }
39118     },
39119     
39120     leave: function(e, el)
39121     {
39122         e.preventDefault();
39123         
39124         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39125             return;
39126         }
39127         
39128         if(this.bgimage.length && this.html.length){
39129             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39130         }
39131     },
39132     
39133     onTouchStart: function(e, el)
39134     {
39135 //        e.preventDefault();
39136         
39137         this.touchmoved = false;
39138         
39139         if(!this.isFitContainer){
39140             return;
39141         }
39142         
39143         if(!this.bgimage.length || !this.html.length){
39144             return;
39145         }
39146         
39147         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39148         
39149         this.timer = new Date().getTime();
39150         
39151     },
39152     
39153     onTouchMove: function(e, el)
39154     {
39155         this.touchmoved = true;
39156     },
39157     
39158     onContextMenu : function(e,el)
39159     {
39160         e.preventDefault();
39161         e.stopPropagation();
39162         return false;
39163     },
39164     
39165     onTouchEnd: function(e, el)
39166     {
39167 //        e.preventDefault();
39168         
39169         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39170         
39171             this.leave(e,el);
39172             
39173             return;
39174         }
39175         
39176         if(!this.bgimage.length || !this.html.length){
39177             
39178             if(this.href.length){
39179                 window.location.href = this.href;
39180             }
39181             
39182             return;
39183         }
39184         
39185         if(!this.isFitContainer){
39186             return;
39187         }
39188         
39189         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39190         
39191         window.location.href = this.href;
39192     },
39193     
39194     //selection on single brick only
39195     selectBrick : function() {
39196         
39197         if (!this.parentId) {
39198             return;
39199         }
39200         
39201         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39202         var index = m.selectedBrick.indexOf(this.id);
39203         
39204         if ( index > -1) {
39205             m.selectedBrick.splice(index,1);
39206             this.el.removeClass(this.activeClass);
39207             return;
39208         }
39209         
39210         for(var i = 0; i < m.selectedBrick.length; i++) {
39211             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39212             b.el.removeClass(b.activeClass);
39213         }
39214         
39215         m.selectedBrick = [];
39216         
39217         m.selectedBrick.push(this.id);
39218         this.el.addClass(this.activeClass);
39219         return;
39220     },
39221     
39222     isSelected : function(){
39223         return this.el.hasClass(this.activeClass);
39224         
39225     }
39226 });
39227
39228 Roo.apply(Roo.bootstrap.MasonryBrick, {
39229     
39230     //groups: {},
39231     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39232      /**
39233     * register a Masonry Brick
39234     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39235     */
39236     
39237     register : function(brick)
39238     {
39239         //this.groups[brick.id] = brick;
39240         this.groups.add(brick.id, brick);
39241     },
39242     /**
39243     * fetch a  masonry brick based on the masonry brick ID
39244     * @param {string} the masonry brick to add
39245     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39246     */
39247     
39248     get: function(brick_id) 
39249     {
39250         // if (typeof(this.groups[brick_id]) == 'undefined') {
39251         //     return false;
39252         // }
39253         // return this.groups[brick_id] ;
39254         
39255         if(this.groups.key(brick_id)) {
39256             return this.groups.key(brick_id);
39257         }
39258         
39259         return false;
39260     }
39261     
39262     
39263     
39264 });
39265
39266  /*
39267  * - LGPL
39268  *
39269  * element
39270  * 
39271  */
39272
39273 /**
39274  * @class Roo.bootstrap.Brick
39275  * @extends Roo.bootstrap.Component
39276  * Bootstrap Brick class
39277  * 
39278  * @constructor
39279  * Create a new Brick
39280  * @param {Object} config The config object
39281  */
39282
39283 Roo.bootstrap.Brick = function(config){
39284     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39285     
39286     this.addEvents({
39287         // raw events
39288         /**
39289          * @event click
39290          * When a Brick is click
39291          * @param {Roo.bootstrap.Brick} this
39292          * @param {Roo.EventObject} e
39293          */
39294         "click" : true
39295     });
39296 };
39297
39298 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39299     
39300     /**
39301      * @cfg {String} title
39302      */   
39303     title : '',
39304     /**
39305      * @cfg {String} html
39306      */   
39307     html : '',
39308     /**
39309      * @cfg {String} bgimage
39310      */   
39311     bgimage : '',
39312     /**
39313      * @cfg {String} cls
39314      */   
39315     cls : '',
39316     /**
39317      * @cfg {String} href
39318      */   
39319     href : '',
39320     /**
39321      * @cfg {String} video
39322      */   
39323     video : '',
39324     /**
39325      * @cfg {Boolean} square
39326      */   
39327     square : true,
39328     
39329     getAutoCreate : function()
39330     {
39331         var cls = 'roo-brick';
39332         
39333         if(this.href.length){
39334             cls += ' roo-brick-link';
39335         }
39336         
39337         if(this.bgimage.length){
39338             cls += ' roo-brick-image';
39339         }
39340         
39341         if(!this.html.length && !this.bgimage.length){
39342             cls += ' roo-brick-center-title';
39343         }
39344         
39345         if(!this.html.length && this.bgimage.length){
39346             cls += ' roo-brick-bottom-title';
39347         }
39348         
39349         if(this.cls){
39350             cls += ' ' + this.cls;
39351         }
39352         
39353         var cfg = {
39354             tag: (this.href.length) ? 'a' : 'div',
39355             cls: cls,
39356             cn: [
39357                 {
39358                     tag: 'div',
39359                     cls: 'roo-brick-paragraph',
39360                     cn: []
39361                 }
39362             ]
39363         };
39364         
39365         if(this.href.length){
39366             cfg.href = this.href;
39367         }
39368         
39369         var cn = cfg.cn[0].cn;
39370         
39371         if(this.title.length){
39372             cn.push({
39373                 tag: 'h4',
39374                 cls: 'roo-brick-title',
39375                 html: this.title
39376             });
39377         }
39378         
39379         if(this.html.length){
39380             cn.push({
39381                 tag: 'p',
39382                 cls: 'roo-brick-text',
39383                 html: this.html
39384             });
39385         } else {
39386             cn.cls += ' hide';
39387         }
39388         
39389         if(this.bgimage.length){
39390             cfg.cn.push({
39391                 tag: 'img',
39392                 cls: 'roo-brick-image-view',
39393                 src: this.bgimage
39394             });
39395         }
39396         
39397         return cfg;
39398     },
39399     
39400     initEvents: function() 
39401     {
39402         if(this.title.length || this.html.length){
39403             this.el.on('mouseenter'  ,this.enter, this);
39404             this.el.on('mouseleave', this.leave, this);
39405         }
39406         
39407         Roo.EventManager.onWindowResize(this.resize, this); 
39408         
39409         if(this.bgimage.length){
39410             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39411             this.imageEl.on('load', this.onImageLoad, this);
39412             return;
39413         }
39414         
39415         this.resize();
39416     },
39417     
39418     onImageLoad : function()
39419     {
39420         this.resize();
39421     },
39422     
39423     resize : function()
39424     {
39425         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39426         
39427         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39428         
39429         if(this.bgimage.length){
39430             var image = this.el.select('.roo-brick-image-view', true).first();
39431             
39432             image.setWidth(paragraph.getWidth());
39433             
39434             if(this.square){
39435                 image.setHeight(paragraph.getWidth());
39436             }
39437             
39438             this.el.setHeight(image.getHeight());
39439             paragraph.setHeight(image.getHeight());
39440             
39441         }
39442         
39443     },
39444     
39445     enter: function(e, el)
39446     {
39447         e.preventDefault();
39448         
39449         if(this.bgimage.length){
39450             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39451             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39452         }
39453     },
39454     
39455     leave: function(e, el)
39456     {
39457         e.preventDefault();
39458         
39459         if(this.bgimage.length){
39460             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39461             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39462         }
39463     }
39464     
39465 });
39466
39467  
39468
39469  /*
39470  * - LGPL
39471  *
39472  * Number field 
39473  */
39474
39475 /**
39476  * @class Roo.bootstrap.form.NumberField
39477  * @extends Roo.bootstrap.form.Input
39478  * Bootstrap NumberField class
39479  * 
39480  * 
39481  * 
39482  * 
39483  * @constructor
39484  * Create a new NumberField
39485  * @param {Object} config The config object
39486  */
39487
39488 Roo.bootstrap.form.NumberField = function(config){
39489     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39490 };
39491
39492 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39493     
39494     /**
39495      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39496      */
39497     allowDecimals : true,
39498     /**
39499      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39500      */
39501     decimalSeparator : ".",
39502     /**
39503      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39504      */
39505     decimalPrecision : 2,
39506     /**
39507      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39508      */
39509     allowNegative : true,
39510     
39511     /**
39512      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39513      */
39514     allowZero: true,
39515     /**
39516      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39517      */
39518     minValue : Number.NEGATIVE_INFINITY,
39519     /**
39520      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39521      */
39522     maxValue : Number.MAX_VALUE,
39523     /**
39524      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39525      */
39526     minText : "The minimum value for this field is {0}",
39527     /**
39528      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39529      */
39530     maxText : "The maximum value for this field is {0}",
39531     /**
39532      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39533      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39534      */
39535     nanText : "{0} is not a valid number",
39536     /**
39537      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39538      */
39539     thousandsDelimiter : false,
39540     /**
39541      * @cfg {String} valueAlign alignment of value
39542      */
39543     valueAlign : "left",
39544
39545     getAutoCreate : function()
39546     {
39547         var hiddenInput = {
39548             tag: 'input',
39549             type: 'hidden',
39550             id: Roo.id(),
39551             cls: 'hidden-number-input'
39552         };
39553         
39554         if (this.name) {
39555             hiddenInput.name = this.name;
39556         }
39557         
39558         this.name = '';
39559         
39560         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39561         
39562         this.name = hiddenInput.name;
39563         
39564         if(cfg.cn.length > 0) {
39565             cfg.cn.push(hiddenInput);
39566         }
39567         
39568         return cfg;
39569     },
39570
39571     // private
39572     initEvents : function()
39573     {   
39574         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39575         
39576         var allowed = "0123456789";
39577         
39578         if(this.allowDecimals){
39579             allowed += this.decimalSeparator;
39580         }
39581         
39582         if(this.allowNegative){
39583             allowed += "-";
39584         }
39585         
39586         if(this.thousandsDelimiter) {
39587             allowed += ",";
39588         }
39589         
39590         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39591         
39592         var keyPress = function(e){
39593             
39594             var k = e.getKey();
39595             
39596             var c = e.getCharCode();
39597             
39598             if(
39599                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39600                     allowed.indexOf(String.fromCharCode(c)) === -1
39601             ){
39602                 e.stopEvent();
39603                 return;
39604             }
39605             
39606             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39607                 return;
39608             }
39609             
39610             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39611                 e.stopEvent();
39612             }
39613         };
39614         
39615         this.el.on("keypress", keyPress, this);
39616     },
39617     
39618     validateValue : function(value)
39619     {
39620         
39621         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39622             return false;
39623         }
39624         
39625         var num = this.parseValue(value);
39626         
39627         if(isNaN(num)){
39628             this.markInvalid(String.format(this.nanText, value));
39629             return false;
39630         }
39631         
39632         if(num < this.minValue){
39633             this.markInvalid(String.format(this.minText, this.minValue));
39634             return false;
39635         }
39636         
39637         if(num > this.maxValue){
39638             this.markInvalid(String.format(this.maxText, this.maxValue));
39639             return false;
39640         }
39641         
39642         return true;
39643     },
39644
39645     getValue : function()
39646     {
39647         var v = this.hiddenEl().getValue();
39648         
39649         return this.fixPrecision(this.parseValue(v));
39650     },
39651
39652     parseValue : function(value)
39653     {
39654         if(this.thousandsDelimiter) {
39655             value += "";
39656             r = new RegExp(",", "g");
39657             value = value.replace(r, "");
39658         }
39659         
39660         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39661         return isNaN(value) ? '' : value;
39662     },
39663
39664     fixPrecision : function(value)
39665     {
39666         if(this.thousandsDelimiter) {
39667             value += "";
39668             r = new RegExp(",", "g");
39669             value = value.replace(r, "");
39670         }
39671         
39672         var nan = isNaN(value);
39673         
39674         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39675             return nan ? '' : value;
39676         }
39677         return parseFloat(value).toFixed(this.decimalPrecision);
39678     },
39679
39680     setValue : function(v)
39681     {
39682         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39683         
39684         this.value = v;
39685         
39686         if(this.rendered){
39687             
39688             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39689             
39690             this.inputEl().dom.value = (v == '') ? '' :
39691                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39692             
39693             if(!this.allowZero && v === '0') {
39694                 this.hiddenEl().dom.value = '';
39695                 this.inputEl().dom.value = '';
39696             }
39697             
39698             this.validate();
39699         }
39700     },
39701
39702     decimalPrecisionFcn : function(v)
39703     {
39704         return Math.floor(v);
39705     },
39706
39707     beforeBlur : function()
39708     {
39709         var v = this.parseValue(this.getRawValue());
39710         
39711         if(v || v === 0 || v === ''){
39712             this.setValue(v);
39713         }
39714     },
39715     
39716     hiddenEl : function()
39717     {
39718         return this.el.select('input.hidden-number-input',true).first();
39719     }
39720     
39721 });
39722
39723  
39724
39725 /*
39726 * Licence: LGPL
39727 */
39728
39729 /**
39730  * @class Roo.bootstrap.DocumentSlider
39731  * @extends Roo.bootstrap.Component
39732  * Bootstrap DocumentSlider class
39733  * 
39734  * @constructor
39735  * Create a new DocumentViewer
39736  * @param {Object} config The config object
39737  */
39738
39739 Roo.bootstrap.DocumentSlider = function(config){
39740     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39741     
39742     this.files = [];
39743     
39744     this.addEvents({
39745         /**
39746          * @event initial
39747          * Fire after initEvent
39748          * @param {Roo.bootstrap.DocumentSlider} this
39749          */
39750         "initial" : true,
39751         /**
39752          * @event update
39753          * Fire after update
39754          * @param {Roo.bootstrap.DocumentSlider} this
39755          */
39756         "update" : true,
39757         /**
39758          * @event click
39759          * Fire after click
39760          * @param {Roo.bootstrap.DocumentSlider} this
39761          */
39762         "click" : true
39763     });
39764 };
39765
39766 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39767     
39768     files : false,
39769     
39770     indicator : 0,
39771     
39772     getAutoCreate : function()
39773     {
39774         var cfg = {
39775             tag : 'div',
39776             cls : 'roo-document-slider',
39777             cn : [
39778                 {
39779                     tag : 'div',
39780                     cls : 'roo-document-slider-header',
39781                     cn : [
39782                         {
39783                             tag : 'div',
39784                             cls : 'roo-document-slider-header-title'
39785                         }
39786                     ]
39787                 },
39788                 {
39789                     tag : 'div',
39790                     cls : 'roo-document-slider-body',
39791                     cn : [
39792                         {
39793                             tag : 'div',
39794                             cls : 'roo-document-slider-prev',
39795                             cn : [
39796                                 {
39797                                     tag : 'i',
39798                                     cls : 'fa fa-chevron-left'
39799                                 }
39800                             ]
39801                         },
39802                         {
39803                             tag : 'div',
39804                             cls : 'roo-document-slider-thumb',
39805                             cn : [
39806                                 {
39807                                     tag : 'img',
39808                                     cls : 'roo-document-slider-image'
39809                                 }
39810                             ]
39811                         },
39812                         {
39813                             tag : 'div',
39814                             cls : 'roo-document-slider-next',
39815                             cn : [
39816                                 {
39817                                     tag : 'i',
39818                                     cls : 'fa fa-chevron-right'
39819                                 }
39820                             ]
39821                         }
39822                     ]
39823                 }
39824             ]
39825         };
39826         
39827         return cfg;
39828     },
39829     
39830     initEvents : function()
39831     {
39832         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39833         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39834         
39835         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39836         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39837         
39838         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39839         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39840         
39841         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39842         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39843         
39844         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39845         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39846         
39847         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39848         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39849         
39850         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39851         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39852         
39853         this.thumbEl.on('click', this.onClick, this);
39854         
39855         this.prevIndicator.on('click', this.prev, this);
39856         
39857         this.nextIndicator.on('click', this.next, this);
39858         
39859     },
39860     
39861     initial : function()
39862     {
39863         if(this.files.length){
39864             this.indicator = 1;
39865             this.update()
39866         }
39867         
39868         this.fireEvent('initial', this);
39869     },
39870     
39871     update : function()
39872     {
39873         this.imageEl.attr('src', this.files[this.indicator - 1]);
39874         
39875         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39876         
39877         this.prevIndicator.show();
39878         
39879         if(this.indicator == 1){
39880             this.prevIndicator.hide();
39881         }
39882         
39883         this.nextIndicator.show();
39884         
39885         if(this.indicator == this.files.length){
39886             this.nextIndicator.hide();
39887         }
39888         
39889         this.thumbEl.scrollTo('top');
39890         
39891         this.fireEvent('update', this);
39892     },
39893     
39894     onClick : function(e)
39895     {
39896         e.preventDefault();
39897         
39898         this.fireEvent('click', this);
39899     },
39900     
39901     prev : function(e)
39902     {
39903         e.preventDefault();
39904         
39905         this.indicator = Math.max(1, this.indicator - 1);
39906         
39907         this.update();
39908     },
39909     
39910     next : function(e)
39911     {
39912         e.preventDefault();
39913         
39914         this.indicator = Math.min(this.files.length, this.indicator + 1);
39915         
39916         this.update();
39917     }
39918 });
39919 /*
39920  * - LGPL
39921  *
39922  * RadioSet
39923  *
39924  *
39925  */
39926
39927 /**
39928  * @class Roo.bootstrap.form.RadioSet
39929  * @extends Roo.bootstrap.form.Input
39930  * @children Roo.bootstrap.form.Radio
39931  * Bootstrap RadioSet class
39932  * @cfg {String} indicatorpos (left|right) default left
39933  * @cfg {Boolean} inline (true|false) inline the element (default true)
39934  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39935  * @constructor
39936  * Create a new RadioSet
39937  * @param {Object} config The config object
39938  */
39939
39940 Roo.bootstrap.form.RadioSet = function(config){
39941     
39942     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39943     
39944     this.radioes = [];
39945     
39946     Roo.bootstrap.form.RadioSet.register(this);
39947     
39948     this.addEvents({
39949         /**
39950         * @event check
39951         * Fires when the element is checked or unchecked.
39952         * @param {Roo.bootstrap.form.RadioSet} this This radio
39953         * @param {Roo.bootstrap.form.Radio} item The checked item
39954         */
39955        check : true,
39956        /**
39957         * @event click
39958         * Fires when the element is click.
39959         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39960         * @param {Roo.bootstrap.form.Radio} item The checked item
39961         * @param {Roo.EventObject} e The event object
39962         */
39963        click : true
39964     });
39965     
39966 };
39967
39968 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39969
39970     radioes : false,
39971     
39972     inline : true,
39973     
39974     weight : '',
39975     
39976     indicatorpos : 'left',
39977     
39978     getAutoCreate : function()
39979     {
39980         var label = {
39981             tag : 'label',
39982             cls : 'roo-radio-set-label',
39983             cn : [
39984                 {
39985                     tag : 'span',
39986                     html : this.fieldLabel
39987                 }
39988             ]
39989         };
39990         if (Roo.bootstrap.version == 3) {
39991             
39992             
39993             if(this.indicatorpos == 'left'){
39994                 label.cn.unshift({
39995                     tag : 'i',
39996                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39997                     tooltip : 'This field is required'
39998                 });
39999             } else {
40000                 label.cn.push({
40001                     tag : 'i',
40002                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
40003                     tooltip : 'This field is required'
40004                 });
40005             }
40006         }
40007         var items = {
40008             tag : 'div',
40009             cls : 'roo-radio-set-items'
40010         };
40011         
40012         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40013         
40014         if (align === 'left' && this.fieldLabel.length) {
40015             
40016             items = {
40017                 cls : "roo-radio-set-right", 
40018                 cn: [
40019                     items
40020                 ]
40021             };
40022             
40023             if(this.labelWidth > 12){
40024                 label.style = "width: " + this.labelWidth + 'px';
40025             }
40026             
40027             if(this.labelWidth < 13 && this.labelmd == 0){
40028                 this.labelmd = this.labelWidth;
40029             }
40030             
40031             if(this.labellg > 0){
40032                 label.cls += ' col-lg-' + this.labellg;
40033                 items.cls += ' col-lg-' + (12 - this.labellg);
40034             }
40035             
40036             if(this.labelmd > 0){
40037                 label.cls += ' col-md-' + this.labelmd;
40038                 items.cls += ' col-md-' + (12 - this.labelmd);
40039             }
40040             
40041             if(this.labelsm > 0){
40042                 label.cls += ' col-sm-' + this.labelsm;
40043                 items.cls += ' col-sm-' + (12 - this.labelsm);
40044             }
40045             
40046             if(this.labelxs > 0){
40047                 label.cls += ' col-xs-' + this.labelxs;
40048                 items.cls += ' col-xs-' + (12 - this.labelxs);
40049             }
40050         }
40051         
40052         var cfg = {
40053             tag : 'div',
40054             cls : 'roo-radio-set',
40055             cn : [
40056                 {
40057                     tag : 'input',
40058                     cls : 'roo-radio-set-input',
40059                     type : 'hidden',
40060                     name : this.name,
40061                     value : this.value ? this.value :  ''
40062                 },
40063                 label,
40064                 items
40065             ]
40066         };
40067         
40068         if(this.weight.length){
40069             cfg.cls += ' roo-radio-' + this.weight;
40070         }
40071         
40072         if(this.inline) {
40073             cfg.cls += ' roo-radio-set-inline';
40074         }
40075         
40076         var settings=this;
40077         ['xs','sm','md','lg'].map(function(size){
40078             if (settings[size]) {
40079                 cfg.cls += ' col-' + size + '-' + settings[size];
40080             }
40081         });
40082         
40083         return cfg;
40084         
40085     },
40086
40087     initEvents : function()
40088     {
40089         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40090         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40091         
40092         if(!this.fieldLabel.length){
40093             this.labelEl.hide();
40094         }
40095         
40096         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40097         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40098         
40099         this.indicator = this.indicatorEl();
40100         
40101         if(this.indicator){
40102             this.indicator.addClass('invisible');
40103         }
40104         
40105         this.originalValue = this.getValue();
40106         
40107     },
40108     
40109     inputEl: function ()
40110     {
40111         return this.el.select('.roo-radio-set-input', true).first();
40112     },
40113     
40114     getChildContainer : function()
40115     {
40116         return this.itemsEl;
40117     },
40118     
40119     register : function(item)
40120     {
40121         this.radioes.push(item);
40122         
40123     },
40124     
40125     validate : function()
40126     {   
40127         if(this.getVisibilityEl().hasClass('hidden')){
40128             return true;
40129         }
40130         
40131         var valid = false;
40132         
40133         Roo.each(this.radioes, function(i){
40134             if(!i.checked){
40135                 return;
40136             }
40137             
40138             valid = true;
40139             return false;
40140         });
40141         
40142         if(this.allowBlank) {
40143             return true;
40144         }
40145         
40146         if(this.disabled || valid){
40147             this.markValid();
40148             return true;
40149         }
40150         
40151         this.markInvalid();
40152         return false;
40153         
40154     },
40155     
40156     markValid : function()
40157     {
40158         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40159             this.indicatorEl().removeClass('visible');
40160             this.indicatorEl().addClass('invisible');
40161         }
40162         
40163         
40164         if (Roo.bootstrap.version == 3) {
40165             this.el.removeClass([this.invalidClass, this.validClass]);
40166             this.el.addClass(this.validClass);
40167         } else {
40168             this.el.removeClass(['is-invalid','is-valid']);
40169             this.el.addClass(['is-valid']);
40170         }
40171         this.fireEvent('valid', this);
40172     },
40173     
40174     markInvalid : function(msg)
40175     {
40176         if(this.allowBlank || this.disabled){
40177             return;
40178         }
40179         
40180         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40181             this.indicatorEl().removeClass('invisible');
40182             this.indicatorEl().addClass('visible');
40183         }
40184         if (Roo.bootstrap.version == 3) {
40185             this.el.removeClass([this.invalidClass, this.validClass]);
40186             this.el.addClass(this.invalidClass);
40187         } else {
40188             this.el.removeClass(['is-invalid','is-valid']);
40189             this.el.addClass(['is-invalid']);
40190         }
40191         
40192         this.fireEvent('invalid', this, msg);
40193         
40194     },
40195     
40196     setValue : function(v, suppressEvent)
40197     {   
40198         if(this.value === v){
40199             return;
40200         }
40201         
40202         this.value = v;
40203         
40204         if(this.rendered){
40205             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40206         }
40207         
40208         Roo.each(this.radioes, function(i){
40209             i.checked = false;
40210             i.el.removeClass('checked');
40211         });
40212         
40213         Roo.each(this.radioes, function(i){
40214             
40215             if(i.value === v || i.value.toString() === v.toString()){
40216                 i.checked = true;
40217                 i.el.addClass('checked');
40218                 
40219                 if(suppressEvent !== true){
40220                     this.fireEvent('check', this, i);
40221                 }
40222                 
40223                 return false;
40224             }
40225             
40226         }, this);
40227         
40228         this.validate();
40229     },
40230     
40231     clearInvalid : function(){
40232         
40233         if(!this.el || this.preventMark){
40234             return;
40235         }
40236         
40237         this.el.removeClass([this.invalidClass]);
40238         
40239         this.fireEvent('valid', this);
40240     }
40241     
40242 });
40243
40244 Roo.apply(Roo.bootstrap.form.RadioSet, {
40245     
40246     groups: {},
40247     
40248     register : function(set)
40249     {
40250         this.groups[set.name] = set;
40251     },
40252     
40253     get: function(name) 
40254     {
40255         if (typeof(this.groups[name]) == 'undefined') {
40256             return false;
40257         }
40258         
40259         return this.groups[name] ;
40260     }
40261     
40262 });
40263 /*
40264  * Based on:
40265  * Ext JS Library 1.1.1
40266  * Copyright(c) 2006-2007, Ext JS, LLC.
40267  *
40268  * Originally Released Under LGPL - original licence link has changed is not relivant.
40269  *
40270  * Fork - LGPL
40271  * <script type="text/javascript">
40272  */
40273
40274
40275 /**
40276  * @class Roo.bootstrap.SplitBar
40277  * @extends Roo.util.Observable
40278  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40279  * <br><br>
40280  * Usage:
40281  * <pre><code>
40282 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40283                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40284 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40285 split.minSize = 100;
40286 split.maxSize = 600;
40287 split.animate = true;
40288 split.on('moved', splitterMoved);
40289 </code></pre>
40290  * @constructor
40291  * Create a new SplitBar
40292  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40293  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40294  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40295  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40296                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40297                         position of the SplitBar).
40298  */
40299 Roo.bootstrap.SplitBar = function(cfg){
40300     
40301     /** @private */
40302     
40303     //{
40304     //  dragElement : elm
40305     //  resizingElement: el,
40306         // optional..
40307     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40308     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40309         // existingProxy ???
40310     //}
40311     
40312     this.el = Roo.get(cfg.dragElement, true);
40313     this.el.dom.unselectable = "on";
40314     /** @private */
40315     this.resizingEl = Roo.get(cfg.resizingElement, true);
40316
40317     /**
40318      * @private
40319      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40320      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40321      * @type Number
40322      */
40323     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40324     
40325     /**
40326      * The minimum size of the resizing element. (Defaults to 0)
40327      * @type Number
40328      */
40329     this.minSize = 0;
40330     
40331     /**
40332      * The maximum size of the resizing element. (Defaults to 2000)
40333      * @type Number
40334      */
40335     this.maxSize = 2000;
40336     
40337     /**
40338      * Whether to animate the transition to the new size
40339      * @type Boolean
40340      */
40341     this.animate = false;
40342     
40343     /**
40344      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40345      * @type Boolean
40346      */
40347     this.useShim = false;
40348     
40349     /** @private */
40350     this.shim = null;
40351     
40352     if(!cfg.existingProxy){
40353         /** @private */
40354         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40355     }else{
40356         this.proxy = Roo.get(cfg.existingProxy).dom;
40357     }
40358     /** @private */
40359     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40360     
40361     /** @private */
40362     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40363     
40364     /** @private */
40365     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40366     
40367     /** @private */
40368     this.dragSpecs = {};
40369     
40370     /**
40371      * @private The adapter to use to positon and resize elements
40372      */
40373     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40374     this.adapter.init(this);
40375     
40376     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40377         /** @private */
40378         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40379         this.el.addClass("roo-splitbar-h");
40380     }else{
40381         /** @private */
40382         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40383         this.el.addClass("roo-splitbar-v");
40384     }
40385     
40386     this.addEvents({
40387         /**
40388          * @event resize
40389          * Fires when the splitter is moved (alias for {@link #event-moved})
40390          * @param {Roo.bootstrap.SplitBar} this
40391          * @param {Number} newSize the new width or height
40392          */
40393         "resize" : true,
40394         /**
40395          * @event moved
40396          * Fires when the splitter is moved
40397          * @param {Roo.bootstrap.SplitBar} this
40398          * @param {Number} newSize the new width or height
40399          */
40400         "moved" : true,
40401         /**
40402          * @event beforeresize
40403          * Fires before the splitter is dragged
40404          * @param {Roo.bootstrap.SplitBar} this
40405          */
40406         "beforeresize" : true,
40407
40408         "beforeapply" : true
40409     });
40410
40411     Roo.util.Observable.call(this);
40412 };
40413
40414 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40415     onStartProxyDrag : function(x, y){
40416         this.fireEvent("beforeresize", this);
40417         if(!this.overlay){
40418             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40419             o.unselectable();
40420             o.enableDisplayMode("block");
40421             // all splitbars share the same overlay
40422             Roo.bootstrap.SplitBar.prototype.overlay = o;
40423         }
40424         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40425         this.overlay.show();
40426         Roo.get(this.proxy).setDisplayed("block");
40427         var size = this.adapter.getElementSize(this);
40428         this.activeMinSize = this.getMinimumSize();;
40429         this.activeMaxSize = this.getMaximumSize();;
40430         var c1 = size - this.activeMinSize;
40431         var c2 = Math.max(this.activeMaxSize - size, 0);
40432         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40433             this.dd.resetConstraints();
40434             this.dd.setXConstraint(
40435                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40436                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40437             );
40438             this.dd.setYConstraint(0, 0);
40439         }else{
40440             this.dd.resetConstraints();
40441             this.dd.setXConstraint(0, 0);
40442             this.dd.setYConstraint(
40443                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40444                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40445             );
40446          }
40447         this.dragSpecs.startSize = size;
40448         this.dragSpecs.startPoint = [x, y];
40449         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40450     },
40451     
40452     /** 
40453      * @private Called after the drag operation by the DDProxy
40454      */
40455     onEndProxyDrag : function(e){
40456         Roo.get(this.proxy).setDisplayed(false);
40457         var endPoint = Roo.lib.Event.getXY(e);
40458         if(this.overlay){
40459             this.overlay.hide();
40460         }
40461         var newSize;
40462         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40463             newSize = this.dragSpecs.startSize + 
40464                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40465                     endPoint[0] - this.dragSpecs.startPoint[0] :
40466                     this.dragSpecs.startPoint[0] - endPoint[0]
40467                 );
40468         }else{
40469             newSize = this.dragSpecs.startSize + 
40470                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40471                     endPoint[1] - this.dragSpecs.startPoint[1] :
40472                     this.dragSpecs.startPoint[1] - endPoint[1]
40473                 );
40474         }
40475         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40476         if(newSize != this.dragSpecs.startSize){
40477             if(this.fireEvent('beforeapply', this, newSize) !== false){
40478                 this.adapter.setElementSize(this, newSize);
40479                 this.fireEvent("moved", this, newSize);
40480                 this.fireEvent("resize", this, newSize);
40481             }
40482         }
40483     },
40484     
40485     /**
40486      * Get the adapter this SplitBar uses
40487      * @return The adapter object
40488      */
40489     getAdapter : function(){
40490         return this.adapter;
40491     },
40492     
40493     /**
40494      * Set the adapter this SplitBar uses
40495      * @param {Object} adapter A SplitBar adapter object
40496      */
40497     setAdapter : function(adapter){
40498         this.adapter = adapter;
40499         this.adapter.init(this);
40500     },
40501     
40502     /**
40503      * Gets the minimum size for the resizing element
40504      * @return {Number} The minimum size
40505      */
40506     getMinimumSize : function(){
40507         return this.minSize;
40508     },
40509     
40510     /**
40511      * Sets the minimum size for the resizing element
40512      * @param {Number} minSize The minimum size
40513      */
40514     setMinimumSize : function(minSize){
40515         this.minSize = minSize;
40516     },
40517     
40518     /**
40519      * Gets the maximum size for the resizing element
40520      * @return {Number} The maximum size
40521      */
40522     getMaximumSize : function(){
40523         return this.maxSize;
40524     },
40525     
40526     /**
40527      * Sets the maximum size for the resizing element
40528      * @param {Number} maxSize The maximum size
40529      */
40530     setMaximumSize : function(maxSize){
40531         this.maxSize = maxSize;
40532     },
40533     
40534     /**
40535      * Sets the initialize size for the resizing element
40536      * @param {Number} size The initial size
40537      */
40538     setCurrentSize : function(size){
40539         var oldAnimate = this.animate;
40540         this.animate = false;
40541         this.adapter.setElementSize(this, size);
40542         this.animate = oldAnimate;
40543     },
40544     
40545     /**
40546      * Destroy this splitbar. 
40547      * @param {Boolean} removeEl True to remove the element
40548      */
40549     destroy : function(removeEl){
40550         if(this.shim){
40551             this.shim.remove();
40552         }
40553         this.dd.unreg();
40554         this.proxy.parentNode.removeChild(this.proxy);
40555         if(removeEl){
40556             this.el.remove();
40557         }
40558     }
40559 });
40560
40561 /**
40562  * @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.
40563  */
40564 Roo.bootstrap.SplitBar.createProxy = function(dir){
40565     var proxy = new Roo.Element(document.createElement("div"));
40566     proxy.unselectable();
40567     var cls = 'roo-splitbar-proxy';
40568     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40569     document.body.appendChild(proxy.dom);
40570     return proxy.dom;
40571 };
40572
40573 /** 
40574  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40575  * Default Adapter. It assumes the splitter and resizing element are not positioned
40576  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40577  */
40578 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40579 };
40580
40581 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40582     // do nothing for now
40583     init : function(s){
40584     
40585     },
40586     /**
40587      * Called before drag operations to get the current size of the resizing element. 
40588      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40589      */
40590      getElementSize : function(s){
40591         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40592             return s.resizingEl.getWidth();
40593         }else{
40594             return s.resizingEl.getHeight();
40595         }
40596     },
40597     
40598     /**
40599      * Called after drag operations to set the size of the resizing element.
40600      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40601      * @param {Number} newSize The new size to set
40602      * @param {Function} onComplete A function to be invoked when resizing is complete
40603      */
40604     setElementSize : function(s, newSize, onComplete){
40605         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40606             if(!s.animate){
40607                 s.resizingEl.setWidth(newSize);
40608                 if(onComplete){
40609                     onComplete(s, newSize);
40610                 }
40611             }else{
40612                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40613             }
40614         }else{
40615             
40616             if(!s.animate){
40617                 s.resizingEl.setHeight(newSize);
40618                 if(onComplete){
40619                     onComplete(s, newSize);
40620                 }
40621             }else{
40622                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40623             }
40624         }
40625     }
40626 };
40627
40628 /** 
40629  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40630  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40631  * Adapter that  moves the splitter element to align with the resized sizing element. 
40632  * Used with an absolute positioned SplitBar.
40633  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40634  * document.body, make sure you assign an id to the body element.
40635  */
40636 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40637     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40638     this.container = Roo.get(container);
40639 };
40640
40641 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40642     init : function(s){
40643         this.basic.init(s);
40644     },
40645     
40646     getElementSize : function(s){
40647         return this.basic.getElementSize(s);
40648     },
40649     
40650     setElementSize : function(s, newSize, onComplete){
40651         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40652     },
40653     
40654     moveSplitter : function(s){
40655         var yes = Roo.bootstrap.SplitBar;
40656         switch(s.placement){
40657             case yes.LEFT:
40658                 s.el.setX(s.resizingEl.getRight());
40659                 break;
40660             case yes.RIGHT:
40661                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40662                 break;
40663             case yes.TOP:
40664                 s.el.setY(s.resizingEl.getBottom());
40665                 break;
40666             case yes.BOTTOM:
40667                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40668                 break;
40669         }
40670     }
40671 };
40672
40673 /**
40674  * Orientation constant - Create a vertical SplitBar
40675  * @static
40676  * @type Number
40677  */
40678 Roo.bootstrap.SplitBar.VERTICAL = 1;
40679
40680 /**
40681  * Orientation constant - Create a horizontal SplitBar
40682  * @static
40683  * @type Number
40684  */
40685 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40686
40687 /**
40688  * Placement constant - The resizing element is to the left of the splitter element
40689  * @static
40690  * @type Number
40691  */
40692 Roo.bootstrap.SplitBar.LEFT = 1;
40693
40694 /**
40695  * Placement constant - The resizing element is to the right of the splitter element
40696  * @static
40697  * @type Number
40698  */
40699 Roo.bootstrap.SplitBar.RIGHT = 2;
40700
40701 /**
40702  * Placement constant - The resizing element is positioned above the splitter element
40703  * @static
40704  * @type Number
40705  */
40706 Roo.bootstrap.SplitBar.TOP = 3;
40707
40708 /**
40709  * Placement constant - The resizing element is positioned under splitter element
40710  * @static
40711  * @type Number
40712  */
40713 Roo.bootstrap.SplitBar.BOTTOM = 4;
40714 /*
40715  * Based on:
40716  * Ext JS Library 1.1.1
40717  * Copyright(c) 2006-2007, Ext JS, LLC.
40718  *
40719  * Originally Released Under LGPL - original licence link has changed is not relivant.
40720  *
40721  * Fork - LGPL
40722  * <script type="text/javascript">
40723  */
40724
40725 /**
40726  * @class Roo.bootstrap.layout.Manager
40727  * @extends Roo.bootstrap.Component
40728  * @abstract
40729  * Base class for layout managers.
40730  */
40731 Roo.bootstrap.layout.Manager = function(config)
40732 {
40733     this.monitorWindowResize = true; // do this before we apply configuration.
40734     
40735     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40736
40737
40738
40739
40740
40741     /** false to disable window resize monitoring @type Boolean */
40742     
40743     this.regions = {};
40744     this.addEvents({
40745         /**
40746          * @event layout
40747          * Fires when a layout is performed.
40748          * @param {Roo.LayoutManager} this
40749          */
40750         "layout" : true,
40751         /**
40752          * @event regionresized
40753          * Fires when the user resizes a region.
40754          * @param {Roo.LayoutRegion} region The resized region
40755          * @param {Number} newSize The new size (width for east/west, height for north/south)
40756          */
40757         "regionresized" : true,
40758         /**
40759          * @event regioncollapsed
40760          * Fires when a region is collapsed.
40761          * @param {Roo.LayoutRegion} region The collapsed region
40762          */
40763         "regioncollapsed" : true,
40764         /**
40765          * @event regionexpanded
40766          * Fires when a region is expanded.
40767          * @param {Roo.LayoutRegion} region The expanded region
40768          */
40769         "regionexpanded" : true
40770     });
40771     this.updating = false;
40772
40773     if (config.el) {
40774         this.el = Roo.get(config.el);
40775         this.initEvents();
40776     }
40777
40778 };
40779
40780 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40781
40782
40783     regions : null,
40784
40785     monitorWindowResize : true,
40786
40787
40788     updating : false,
40789
40790
40791     onRender : function(ct, position)
40792     {
40793         if(!this.el){
40794             this.el = Roo.get(ct);
40795             this.initEvents();
40796         }
40797         //this.fireEvent('render',this);
40798     },
40799
40800
40801     initEvents: function()
40802     {
40803
40804
40805         // ie scrollbar fix
40806         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40807             document.body.scroll = "no";
40808         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40809             this.el.position('relative');
40810         }
40811         this.id = this.el.id;
40812         this.el.addClass("roo-layout-container");
40813         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40814         if(this.el.dom != document.body ) {
40815             this.el.on('resize', this.layout,this);
40816             this.el.on('show', this.layout,this);
40817         }
40818
40819     },
40820
40821     /**
40822      * Returns true if this layout is currently being updated
40823      * @return {Boolean}
40824      */
40825     isUpdating : function(){
40826         return this.updating;
40827     },
40828
40829     /**
40830      * Suspend the LayoutManager from doing auto-layouts while
40831      * making multiple add or remove calls
40832      */
40833     beginUpdate : function(){
40834         this.updating = true;
40835     },
40836
40837     /**
40838      * Restore auto-layouts and optionally disable the manager from performing a layout
40839      * @param {Boolean} noLayout true to disable a layout update
40840      */
40841     endUpdate : function(noLayout){
40842         this.updating = false;
40843         if(!noLayout){
40844             this.layout();
40845         }
40846     },
40847
40848     layout: function(){
40849         // abstract...
40850     },
40851
40852     onRegionResized : function(region, newSize){
40853         this.fireEvent("regionresized", region, newSize);
40854         this.layout();
40855     },
40856
40857     onRegionCollapsed : function(region){
40858         this.fireEvent("regioncollapsed", region);
40859     },
40860
40861     onRegionExpanded : function(region){
40862         this.fireEvent("regionexpanded", region);
40863     },
40864
40865     /**
40866      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40867      * performs box-model adjustments.
40868      * @return {Object} The size as an object {width: (the width), height: (the height)}
40869      */
40870     getViewSize : function()
40871     {
40872         var size;
40873         if(this.el.dom != document.body){
40874             size = this.el.getSize();
40875         }else{
40876             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40877         }
40878         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40879         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40880         return size;
40881     },
40882
40883     /**
40884      * Returns the Element this layout is bound to.
40885      * @return {Roo.Element}
40886      */
40887     getEl : function(){
40888         return this.el;
40889     },
40890
40891     /**
40892      * Returns the specified region.
40893      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40894      * @return {Roo.LayoutRegion}
40895      */
40896     getRegion : function(target){
40897         return this.regions[target.toLowerCase()];
40898     },
40899
40900     onWindowResize : function(){
40901         if(this.monitorWindowResize){
40902             this.layout();
40903         }
40904     }
40905 });
40906 /*
40907  * Based on:
40908  * Ext JS Library 1.1.1
40909  * Copyright(c) 2006-2007, Ext JS, LLC.
40910  *
40911  * Originally Released Under LGPL - original licence link has changed is not relivant.
40912  *
40913  * Fork - LGPL
40914  * <script type="text/javascript">
40915  */
40916 /**
40917  * @class Roo.bootstrap.layout.Border
40918  * @extends Roo.bootstrap.layout.Manager
40919  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40920  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40921  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40922  * please see: examples/bootstrap/nested.html<br><br>
40923  
40924 <b>The container the layout is rendered into can be either the body element or any other element.
40925 If it is not the body element, the container needs to either be an absolute positioned element,
40926 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40927 the container size if it is not the body element.</b>
40928
40929 * @constructor
40930 * Create a new Border
40931 * @param {Object} config Configuration options
40932  */
40933 Roo.bootstrap.layout.Border = function(config){
40934     config = config || {};
40935     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40936     
40937     
40938     
40939     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40940         if(config[region]){
40941             config[region].region = region;
40942             this.addRegion(config[region]);
40943         }
40944     },this);
40945     
40946 };
40947
40948 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40949
40950 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40951     
40952         /**
40953          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40954          */
40955         /**
40956          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40957          */
40958         /**
40959          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40960          */
40961         /**
40962          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40963          */
40964         /**
40965          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40966          */
40967         
40968         
40969         
40970         
40971     parent : false, // this might point to a 'nest' or a ???
40972     
40973     /**
40974      * Creates and adds a new region if it doesn't already exist.
40975      * @param {String} target The target region key (north, south, east, west or center).
40976      * @param {Object} config The regions config object
40977      * @return {BorderLayoutRegion} The new region
40978      */
40979     addRegion : function(config)
40980     {
40981         if(!this.regions[config.region]){
40982             var r = this.factory(config);
40983             this.bindRegion(r);
40984         }
40985         return this.regions[config.region];
40986     },
40987
40988     // private (kinda)
40989     bindRegion : function(r){
40990         this.regions[r.config.region] = r;
40991         
40992         r.on("visibilitychange",    this.layout, this);
40993         r.on("paneladded",          this.layout, this);
40994         r.on("panelremoved",        this.layout, this);
40995         r.on("invalidated",         this.layout, this);
40996         r.on("resized",             this.onRegionResized, this);
40997         r.on("collapsed",           this.onRegionCollapsed, this);
40998         r.on("expanded",            this.onRegionExpanded, this);
40999     },
41000
41001     /**
41002      * Performs a layout update.
41003      */
41004     layout : function()
41005     {
41006         if(this.updating) {
41007             return;
41008         }
41009         
41010         // render all the rebions if they have not been done alreayd?
41011         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41012             if(this.regions[region] && !this.regions[region].bodyEl){
41013                 this.regions[region].onRender(this.el)
41014             }
41015         },this);
41016         
41017         var size = this.getViewSize();
41018         var w = size.width;
41019         var h = size.height;
41020         var centerW = w;
41021         var centerH = h;
41022         var centerY = 0;
41023         var centerX = 0;
41024         //var x = 0, y = 0;
41025
41026         var rs = this.regions;
41027         var north = rs["north"];
41028         var south = rs["south"]; 
41029         var west = rs["west"];
41030         var east = rs["east"];
41031         var center = rs["center"];
41032         //if(this.hideOnLayout){ // not supported anymore
41033             //c.el.setStyle("display", "none");
41034         //}
41035         if(north && north.isVisible()){
41036             var b = north.getBox();
41037             var m = north.getMargins();
41038             b.width = w - (m.left+m.right);
41039             b.x = m.left;
41040             b.y = m.top;
41041             centerY = b.height + b.y + m.bottom;
41042             centerH -= centerY;
41043             north.updateBox(this.safeBox(b));
41044         }
41045         if(south && south.isVisible()){
41046             var b = south.getBox();
41047             var m = south.getMargins();
41048             b.width = w - (m.left+m.right);
41049             b.x = m.left;
41050             var totalHeight = (b.height + m.top + m.bottom);
41051             b.y = h - totalHeight + m.top;
41052             centerH -= totalHeight;
41053             south.updateBox(this.safeBox(b));
41054         }
41055         if(west && west.isVisible()){
41056             var b = west.getBox();
41057             var m = west.getMargins();
41058             b.height = centerH - (m.top+m.bottom);
41059             b.x = m.left;
41060             b.y = centerY + m.top;
41061             var totalWidth = (b.width + m.left + m.right);
41062             centerX += totalWidth;
41063             centerW -= totalWidth;
41064             west.updateBox(this.safeBox(b));
41065         }
41066         if(east && east.isVisible()){
41067             var b = east.getBox();
41068             var m = east.getMargins();
41069             b.height = centerH - (m.top+m.bottom);
41070             var totalWidth = (b.width + m.left + m.right);
41071             b.x = w - totalWidth + m.left;
41072             b.y = centerY + m.top;
41073             centerW -= totalWidth;
41074             east.updateBox(this.safeBox(b));
41075         }
41076         if(center){
41077             var m = center.getMargins();
41078             var centerBox = {
41079                 x: centerX + m.left,
41080                 y: centerY + m.top,
41081                 width: centerW - (m.left+m.right),
41082                 height: centerH - (m.top+m.bottom)
41083             };
41084             //if(this.hideOnLayout){
41085                 //center.el.setStyle("display", "block");
41086             //}
41087             center.updateBox(this.safeBox(centerBox));
41088         }
41089         this.el.repaint();
41090         this.fireEvent("layout", this);
41091     },
41092
41093     // private
41094     safeBox : function(box){
41095         box.width = Math.max(0, box.width);
41096         box.height = Math.max(0, box.height);
41097         return box;
41098     },
41099
41100     /**
41101      * Adds a ContentPanel (or subclass) to this layout.
41102      * @param {String} target The target region key (north, south, east, west or center).
41103      * @param {Roo.ContentPanel} panel The panel to add
41104      * @return {Roo.ContentPanel} The added panel
41105      */
41106     add : function(target, panel){
41107          
41108         target = target.toLowerCase();
41109         return this.regions[target].add(panel);
41110     },
41111
41112     /**
41113      * Remove a ContentPanel (or subclass) to this layout.
41114      * @param {String} target The target region key (north, south, east, west or center).
41115      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41116      * @return {Roo.ContentPanel} The removed panel
41117      */
41118     remove : function(target, panel){
41119         target = target.toLowerCase();
41120         return this.regions[target].remove(panel);
41121     },
41122
41123     /**
41124      * Searches all regions for a panel with the specified id
41125      * @param {String} panelId
41126      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41127      */
41128     findPanel : function(panelId){
41129         var rs = this.regions;
41130         for(var target in rs){
41131             if(typeof rs[target] != "function"){
41132                 var p = rs[target].getPanel(panelId);
41133                 if(p){
41134                     return p;
41135                 }
41136             }
41137         }
41138         return null;
41139     },
41140
41141     /**
41142      * Searches all regions for a panel with the specified id and activates (shows) it.
41143      * @param {String/ContentPanel} panelId The panels id or the panel itself
41144      * @return {Roo.ContentPanel} The shown panel or null
41145      */
41146     showPanel : function(panelId) {
41147       var rs = this.regions;
41148       for(var target in rs){
41149          var r = rs[target];
41150          if(typeof r != "function"){
41151             if(r.hasPanel(panelId)){
41152                return r.showPanel(panelId);
41153             }
41154          }
41155       }
41156       return null;
41157    },
41158
41159    /**
41160      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41161      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41162      */
41163    /*
41164     restoreState : function(provider){
41165         if(!provider){
41166             provider = Roo.state.Manager;
41167         }
41168         var sm = new Roo.LayoutStateManager();
41169         sm.init(this, provider);
41170     },
41171 */
41172  
41173  
41174     /**
41175      * Adds a xtype elements to the layout.
41176      * <pre><code>
41177
41178 layout.addxtype({
41179        xtype : 'ContentPanel',
41180        region: 'west',
41181        items: [ .... ]
41182    }
41183 );
41184
41185 layout.addxtype({
41186         xtype : 'NestedLayoutPanel',
41187         region: 'west',
41188         layout: {
41189            center: { },
41190            west: { }   
41191         },
41192         items : [ ... list of content panels or nested layout panels.. ]
41193    }
41194 );
41195 </code></pre>
41196      * @param {Object} cfg Xtype definition of item to add.
41197      */
41198     addxtype : function(cfg)
41199     {
41200         // basically accepts a pannel...
41201         // can accept a layout region..!?!?
41202         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41203         
41204         
41205         // theory?  children can only be panels??
41206         
41207         //if (!cfg.xtype.match(/Panel$/)) {
41208         //    return false;
41209         //}
41210         var ret = false;
41211         
41212         if (typeof(cfg.region) == 'undefined') {
41213             Roo.log("Failed to add Panel, region was not set");
41214             Roo.log(cfg);
41215             return false;
41216         }
41217         var region = cfg.region;
41218         delete cfg.region;
41219         
41220           
41221         var xitems = [];
41222         if (cfg.items) {
41223             xitems = cfg.items;
41224             delete cfg.items;
41225         }
41226         var nb = false;
41227         
41228         if ( region == 'center') {
41229             Roo.log("Center: " + cfg.title);
41230         }
41231         
41232         
41233         switch(cfg.xtype) 
41234         {
41235             case 'Content':  // ContentPanel (el, cfg)
41236             case 'Scroll':  // ContentPanel (el, cfg)
41237             case 'View': 
41238                 cfg.autoCreate = cfg.autoCreate || true;
41239                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41240                 //} else {
41241                 //    var el = this.el.createChild();
41242                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41243                 //}
41244                 
41245                 this.add(region, ret);
41246                 break;
41247             
41248             /*
41249             case 'TreePanel': // our new panel!
41250                 cfg.el = this.el.createChild();
41251                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41252                 this.add(region, ret);
41253                 break;
41254             */
41255             
41256             case 'Nest': 
41257                 // create a new Layout (which is  a Border Layout...
41258                 
41259                 var clayout = cfg.layout;
41260                 clayout.el  = this.el.createChild();
41261                 clayout.items   = clayout.items  || [];
41262                 
41263                 delete cfg.layout;
41264                 
41265                 // replace this exitems with the clayout ones..
41266                 xitems = clayout.items;
41267                  
41268                 // force background off if it's in center...
41269                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41270                     cfg.background = false;
41271                 }
41272                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41273                 
41274                 
41275                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41276                 //console.log('adding nested layout panel '  + cfg.toSource());
41277                 this.add(region, ret);
41278                 nb = {}; /// find first...
41279                 break;
41280             
41281             case 'Grid':
41282                 
41283                 // needs grid and region
41284                 
41285                 //var el = this.getRegion(region).el.createChild();
41286                 /*
41287                  *var el = this.el.createChild();
41288                 // create the grid first...
41289                 cfg.grid.container = el;
41290                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41291                 */
41292                 
41293                 if (region == 'center' && this.active ) {
41294                     cfg.background = false;
41295                 }
41296                 
41297                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41298                 
41299                 this.add(region, ret);
41300                 /*
41301                 if (cfg.background) {
41302                     // render grid on panel activation (if panel background)
41303                     ret.on('activate', function(gp) {
41304                         if (!gp.grid.rendered) {
41305                     //        gp.grid.render(el);
41306                         }
41307                     });
41308                 } else {
41309                   //  cfg.grid.render(el);
41310                 }
41311                 */
41312                 break;
41313            
41314            
41315             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41316                 // it was the old xcomponent building that caused this before.
41317                 // espeically if border is the top element in the tree.
41318                 ret = this;
41319                 break; 
41320                 
41321                     
41322                 
41323                 
41324                 
41325             default:
41326                 /*
41327                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41328                     
41329                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41330                     this.add(region, ret);
41331                 } else {
41332                 */
41333                     Roo.log(cfg);
41334                     throw "Can not add '" + cfg.xtype + "' to Border";
41335                     return null;
41336              
41337                                 
41338              
41339         }
41340         this.beginUpdate();
41341         // add children..
41342         var region = '';
41343         var abn = {};
41344         Roo.each(xitems, function(i)  {
41345             region = nb && i.region ? i.region : false;
41346             
41347             var add = ret.addxtype(i);
41348            
41349             if (region) {
41350                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41351                 if (!i.background) {
41352                     abn[region] = nb[region] ;
41353                 }
41354             }
41355             
41356         });
41357         this.endUpdate();
41358
41359         // make the last non-background panel active..
41360         //if (nb) { Roo.log(abn); }
41361         if (nb) {
41362             
41363             for(var r in abn) {
41364                 region = this.getRegion(r);
41365                 if (region) {
41366                     // tried using nb[r], but it does not work..
41367                      
41368                     region.showPanel(abn[r]);
41369                    
41370                 }
41371             }
41372         }
41373         return ret;
41374         
41375     },
41376     
41377     
41378 // private
41379     factory : function(cfg)
41380     {
41381         
41382         var validRegions = Roo.bootstrap.layout.Border.regions;
41383
41384         var target = cfg.region;
41385         cfg.mgr = this;
41386         
41387         var r = Roo.bootstrap.layout;
41388         Roo.log(target);
41389         switch(target){
41390             case "north":
41391                 return new r.North(cfg);
41392             case "south":
41393                 return new r.South(cfg);
41394             case "east":
41395                 return new r.East(cfg);
41396             case "west":
41397                 return new r.West(cfg);
41398             case "center":
41399                 return new r.Center(cfg);
41400         }
41401         throw 'Layout region "'+target+'" not supported.';
41402     }
41403     
41404     
41405 });
41406  /*
41407  * Based on:
41408  * Ext JS Library 1.1.1
41409  * Copyright(c) 2006-2007, Ext JS, LLC.
41410  *
41411  * Originally Released Under LGPL - original licence link has changed is not relivant.
41412  *
41413  * Fork - LGPL
41414  * <script type="text/javascript">
41415  */
41416  
41417 /**
41418  * @class Roo.bootstrap.layout.Basic
41419  * @extends Roo.util.Observable
41420  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41421  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41422  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41423  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41424  * @cfg {string}   region  the region that it inhabits..
41425  * @cfg {bool}   skipConfig skip config?
41426  * 
41427
41428  */
41429 Roo.bootstrap.layout.Basic = function(config){
41430     
41431     this.mgr = config.mgr;
41432     
41433     this.position = config.region;
41434     
41435     var skipConfig = config.skipConfig;
41436     
41437     this.events = {
41438         /**
41439          * @scope Roo.BasicLayoutRegion
41440          */
41441         
41442         /**
41443          * @event beforeremove
41444          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41445          * @param {Roo.LayoutRegion} this
41446          * @param {Roo.ContentPanel} panel The panel
41447          * @param {Object} e The cancel event object
41448          */
41449         "beforeremove" : true,
41450         /**
41451          * @event invalidated
41452          * Fires when the layout for this region is changed.
41453          * @param {Roo.LayoutRegion} this
41454          */
41455         "invalidated" : true,
41456         /**
41457          * @event visibilitychange
41458          * Fires when this region is shown or hidden 
41459          * @param {Roo.LayoutRegion} this
41460          * @param {Boolean} visibility true or false
41461          */
41462         "visibilitychange" : true,
41463         /**
41464          * @event paneladded
41465          * Fires when a panel is added. 
41466          * @param {Roo.LayoutRegion} this
41467          * @param {Roo.ContentPanel} panel The panel
41468          */
41469         "paneladded" : true,
41470         /**
41471          * @event panelremoved
41472          * Fires when a panel is removed. 
41473          * @param {Roo.LayoutRegion} this
41474          * @param {Roo.ContentPanel} panel The panel
41475          */
41476         "panelremoved" : true,
41477         /**
41478          * @event beforecollapse
41479          * Fires when this region before collapse.
41480          * @param {Roo.LayoutRegion} this
41481          */
41482         "beforecollapse" : true,
41483         /**
41484          * @event collapsed
41485          * Fires when this region is collapsed.
41486          * @param {Roo.LayoutRegion} this
41487          */
41488         "collapsed" : true,
41489         /**
41490          * @event expanded
41491          * Fires when this region is expanded.
41492          * @param {Roo.LayoutRegion} this
41493          */
41494         "expanded" : true,
41495         /**
41496          * @event slideshow
41497          * Fires when this region is slid into view.
41498          * @param {Roo.LayoutRegion} this
41499          */
41500         "slideshow" : true,
41501         /**
41502          * @event slidehide
41503          * Fires when this region slides out of view. 
41504          * @param {Roo.LayoutRegion} this
41505          */
41506         "slidehide" : true,
41507         /**
41508          * @event panelactivated
41509          * Fires when a panel is activated. 
41510          * @param {Roo.LayoutRegion} this
41511          * @param {Roo.ContentPanel} panel The activated panel
41512          */
41513         "panelactivated" : true,
41514         /**
41515          * @event resized
41516          * Fires when the user resizes this region. 
41517          * @param {Roo.LayoutRegion} this
41518          * @param {Number} newSize The new size (width for east/west, height for north/south)
41519          */
41520         "resized" : true
41521     };
41522     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41523     this.panels = new Roo.util.MixedCollection();
41524     this.panels.getKey = this.getPanelId.createDelegate(this);
41525     this.box = null;
41526     this.activePanel = null;
41527     // ensure listeners are added...
41528     
41529     if (config.listeners || config.events) {
41530         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41531             listeners : config.listeners || {},
41532             events : config.events || {}
41533         });
41534     }
41535     
41536     if(skipConfig !== true){
41537         this.applyConfig(config);
41538     }
41539 };
41540
41541 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41542 {
41543     getPanelId : function(p){
41544         return p.getId();
41545     },
41546     
41547     applyConfig : function(config){
41548         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41549         this.config = config;
41550         
41551     },
41552     
41553     /**
41554      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41555      * the width, for horizontal (north, south) the height.
41556      * @param {Number} newSize The new width or height
41557      */
41558     resizeTo : function(newSize){
41559         var el = this.el ? this.el :
41560                  (this.activePanel ? this.activePanel.getEl() : null);
41561         if(el){
41562             switch(this.position){
41563                 case "east":
41564                 case "west":
41565                     el.setWidth(newSize);
41566                     this.fireEvent("resized", this, newSize);
41567                 break;
41568                 case "north":
41569                 case "south":
41570                     el.setHeight(newSize);
41571                     this.fireEvent("resized", this, newSize);
41572                 break;                
41573             }
41574         }
41575     },
41576     
41577     getBox : function(){
41578         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41579     },
41580     
41581     getMargins : function(){
41582         return this.margins;
41583     },
41584     
41585     updateBox : function(box){
41586         this.box = box;
41587         var el = this.activePanel.getEl();
41588         el.dom.style.left = box.x + "px";
41589         el.dom.style.top = box.y + "px";
41590         this.activePanel.setSize(box.width, box.height);
41591     },
41592     
41593     /**
41594      * Returns the container element for this region.
41595      * @return {Roo.Element}
41596      */
41597     getEl : function(){
41598         return this.activePanel;
41599     },
41600     
41601     /**
41602      * Returns true if this region is currently visible.
41603      * @return {Boolean}
41604      */
41605     isVisible : function(){
41606         return this.activePanel ? true : false;
41607     },
41608     
41609     setActivePanel : function(panel){
41610         panel = this.getPanel(panel);
41611         if(this.activePanel && this.activePanel != panel){
41612             this.activePanel.setActiveState(false);
41613             this.activePanel.getEl().setLeftTop(-10000,-10000);
41614         }
41615         this.activePanel = panel;
41616         panel.setActiveState(true);
41617         if(this.box){
41618             panel.setSize(this.box.width, this.box.height);
41619         }
41620         this.fireEvent("panelactivated", this, panel);
41621         this.fireEvent("invalidated");
41622     },
41623     
41624     /**
41625      * Show the specified panel.
41626      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41627      * @return {Roo.ContentPanel} The shown panel or null
41628      */
41629     showPanel : function(panel){
41630         panel = this.getPanel(panel);
41631         if(panel){
41632             this.setActivePanel(panel);
41633         }
41634         return panel;
41635     },
41636     
41637     /**
41638      * Get the active panel for this region.
41639      * @return {Roo.ContentPanel} The active panel or null
41640      */
41641     getActivePanel : function(){
41642         return this.activePanel;
41643     },
41644     
41645     /**
41646      * Add the passed ContentPanel(s)
41647      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41648      * @return {Roo.ContentPanel} The panel added (if only one was added)
41649      */
41650     add : function(panel){
41651         if(arguments.length > 1){
41652             for(var i = 0, len = arguments.length; i < len; i++) {
41653                 this.add(arguments[i]);
41654             }
41655             return null;
41656         }
41657         if(this.hasPanel(panel)){
41658             this.showPanel(panel);
41659             return panel;
41660         }
41661         var el = panel.getEl();
41662         if(el.dom.parentNode != this.mgr.el.dom){
41663             this.mgr.el.dom.appendChild(el.dom);
41664         }
41665         if(panel.setRegion){
41666             panel.setRegion(this);
41667         }
41668         this.panels.add(panel);
41669         el.setStyle("position", "absolute");
41670         if(!panel.background){
41671             this.setActivePanel(panel);
41672             if(this.config.initialSize && this.panels.getCount()==1){
41673                 this.resizeTo(this.config.initialSize);
41674             }
41675         }
41676         this.fireEvent("paneladded", this, panel);
41677         return panel;
41678     },
41679     
41680     /**
41681      * Returns true if the panel is in this region.
41682      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41683      * @return {Boolean}
41684      */
41685     hasPanel : function(panel){
41686         if(typeof panel == "object"){ // must be panel obj
41687             panel = panel.getId();
41688         }
41689         return this.getPanel(panel) ? true : false;
41690     },
41691     
41692     /**
41693      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41694      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41695      * @param {Boolean} preservePanel Overrides the config preservePanel option
41696      * @return {Roo.ContentPanel} The panel that was removed
41697      */
41698     remove : function(panel, preservePanel){
41699         panel = this.getPanel(panel);
41700         if(!panel){
41701             return null;
41702         }
41703         var e = {};
41704         this.fireEvent("beforeremove", this, panel, e);
41705         if(e.cancel === true){
41706             return null;
41707         }
41708         var panelId = panel.getId();
41709         this.panels.removeKey(panelId);
41710         return panel;
41711     },
41712     
41713     /**
41714      * Returns the panel specified or null if it's not in this region.
41715      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41716      * @return {Roo.ContentPanel}
41717      */
41718     getPanel : function(id){
41719         if(typeof id == "object"){ // must be panel obj
41720             return id;
41721         }
41722         return this.panels.get(id);
41723     },
41724     
41725     /**
41726      * Returns this regions position (north/south/east/west/center).
41727      * @return {String} 
41728      */
41729     getPosition: function(){
41730         return this.position;    
41731     }
41732 });/*
41733  * Based on:
41734  * Ext JS Library 1.1.1
41735  * Copyright(c) 2006-2007, Ext JS, LLC.
41736  *
41737  * Originally Released Under LGPL - original licence link has changed is not relivant.
41738  *
41739  * Fork - LGPL
41740  * <script type="text/javascript">
41741  */
41742  
41743 /**
41744  * @class Roo.bootstrap.layout.Region
41745  * @extends Roo.bootstrap.layout.Basic
41746  * This class represents a region in a layout manager.
41747  
41748  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41749  * @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})
41750  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41751  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41752  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41753  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41754  * @cfg {String}    title           The title for the region (overrides panel titles)
41755  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41756  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41757  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41758  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41759  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41760  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41761  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41762  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41763  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41764  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41765
41766  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41767  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41768  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41769  * @cfg {Number}    width           For East/West panels
41770  * @cfg {Number}    height          For North/South panels
41771  * @cfg {Boolean}   split           To show the splitter
41772  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41773  * 
41774  * @cfg {string}   cls             Extra CSS classes to add to region
41775  * 
41776  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41777  * @cfg {string}   region  the region that it inhabits..
41778  *
41779
41780  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41781  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41782
41783  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41784  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41785  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41786  */
41787 Roo.bootstrap.layout.Region = function(config)
41788 {
41789     this.applyConfig(config);
41790
41791     var mgr = config.mgr;
41792     var pos = config.region;
41793     config.skipConfig = true;
41794     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41795     
41796     if (mgr.el) {
41797         this.onRender(mgr.el);   
41798     }
41799      
41800     this.visible = true;
41801     this.collapsed = false;
41802     this.unrendered_panels = [];
41803 };
41804
41805 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41806
41807     position: '', // set by wrapper (eg. north/south etc..)
41808     unrendered_panels : null,  // unrendered panels.
41809     
41810     tabPosition : false,
41811     
41812     mgr: false, // points to 'Border'
41813     
41814     
41815     createBody : function(){
41816         /** This region's body element 
41817         * @type Roo.Element */
41818         this.bodyEl = this.el.createChild({
41819                 tag: "div",
41820                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41821         });
41822     },
41823
41824     onRender: function(ctr, pos)
41825     {
41826         var dh = Roo.DomHelper;
41827         /** This region's container element 
41828         * @type Roo.Element */
41829         this.el = dh.append(ctr.dom, {
41830                 tag: "div",
41831                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41832             }, true);
41833         /** This region's title element 
41834         * @type Roo.Element */
41835     
41836         this.titleEl = dh.append(this.el.dom,  {
41837                 tag: "div",
41838                 unselectable: "on",
41839                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41840                 children:[
41841                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41842                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41843                 ]
41844             }, true);
41845         
41846         this.titleEl.enableDisplayMode();
41847         /** This region's title text element 
41848         * @type HTMLElement */
41849         this.titleTextEl = this.titleEl.dom.firstChild;
41850         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41851         /*
41852         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41853         this.closeBtn.enableDisplayMode();
41854         this.closeBtn.on("click", this.closeClicked, this);
41855         this.closeBtn.hide();
41856     */
41857         this.createBody(this.config);
41858         if(this.config.hideWhenEmpty){
41859             this.hide();
41860             this.on("paneladded", this.validateVisibility, this);
41861             this.on("panelremoved", this.validateVisibility, this);
41862         }
41863         if(this.autoScroll){
41864             this.bodyEl.setStyle("overflow", "auto");
41865         }else{
41866             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41867         }
41868         //if(c.titlebar !== false){
41869             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41870                 this.titleEl.hide();
41871             }else{
41872                 this.titleEl.show();
41873                 if(this.config.title){
41874                     this.titleTextEl.innerHTML = this.config.title;
41875                 }
41876             }
41877         //}
41878         if(this.config.collapsed){
41879             this.collapse(true);
41880         }
41881         if(this.config.hidden){
41882             this.hide();
41883         }
41884         
41885         if (this.unrendered_panels && this.unrendered_panels.length) {
41886             for (var i =0;i< this.unrendered_panels.length; i++) {
41887                 this.add(this.unrendered_panels[i]);
41888             }
41889             this.unrendered_panels = null;
41890             
41891         }
41892         
41893     },
41894     
41895     applyConfig : function(c)
41896     {
41897         /*
41898          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41899             var dh = Roo.DomHelper;
41900             if(c.titlebar !== false){
41901                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41902                 this.collapseBtn.on("click", this.collapse, this);
41903                 this.collapseBtn.enableDisplayMode();
41904                 /*
41905                 if(c.showPin === true || this.showPin){
41906                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41907                     this.stickBtn.enableDisplayMode();
41908                     this.stickBtn.on("click", this.expand, this);
41909                     this.stickBtn.hide();
41910                 }
41911                 
41912             }
41913             */
41914             /** This region's collapsed element
41915             * @type Roo.Element */
41916             /*
41917              *
41918             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41919                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41920             ]}, true);
41921             
41922             if(c.floatable !== false){
41923                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41924                this.collapsedEl.on("click", this.collapseClick, this);
41925             }
41926
41927             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41928                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41929                    id: "message", unselectable: "on", style:{"float":"left"}});
41930                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41931              }
41932             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41933             this.expandBtn.on("click", this.expand, this);
41934             
41935         }
41936         
41937         if(this.collapseBtn){
41938             this.collapseBtn.setVisible(c.collapsible == true);
41939         }
41940         
41941         this.cmargins = c.cmargins || this.cmargins ||
41942                          (this.position == "west" || this.position == "east" ?
41943                              {top: 0, left: 2, right:2, bottom: 0} :
41944                              {top: 2, left: 0, right:0, bottom: 2});
41945         */
41946         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41947         
41948         
41949         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41950         
41951         this.autoScroll = c.autoScroll || false;
41952         
41953         
41954        
41955         
41956         this.duration = c.duration || .30;
41957         this.slideDuration = c.slideDuration || .45;
41958         this.config = c;
41959        
41960     },
41961     /**
41962      * Returns true if this region is currently visible.
41963      * @return {Boolean}
41964      */
41965     isVisible : function(){
41966         return this.visible;
41967     },
41968
41969     /**
41970      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41971      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41972      */
41973     //setCollapsedTitle : function(title){
41974     //    title = title || "&#160;";
41975      //   if(this.collapsedTitleTextEl){
41976       //      this.collapsedTitleTextEl.innerHTML = title;
41977        // }
41978     //},
41979
41980     getBox : function(){
41981         var b;
41982       //  if(!this.collapsed){
41983             b = this.el.getBox(false, true);
41984        // }else{
41985           //  b = this.collapsedEl.getBox(false, true);
41986         //}
41987         return b;
41988     },
41989
41990     getMargins : function(){
41991         return this.margins;
41992         //return this.collapsed ? this.cmargins : this.margins;
41993     },
41994 /*
41995     highlight : function(){
41996         this.el.addClass("x-layout-panel-dragover");
41997     },
41998
41999     unhighlight : function(){
42000         this.el.removeClass("x-layout-panel-dragover");
42001     },
42002 */
42003     updateBox : function(box)
42004     {
42005         if (!this.bodyEl) {
42006             return; // not rendered yet..
42007         }
42008         
42009         this.box = box;
42010         if(!this.collapsed){
42011             this.el.dom.style.left = box.x + "px";
42012             this.el.dom.style.top = box.y + "px";
42013             this.updateBody(box.width, box.height);
42014         }else{
42015             this.collapsedEl.dom.style.left = box.x + "px";
42016             this.collapsedEl.dom.style.top = box.y + "px";
42017             this.collapsedEl.setSize(box.width, box.height);
42018         }
42019         if(this.tabs){
42020             this.tabs.autoSizeTabs();
42021         }
42022     },
42023
42024     updateBody : function(w, h)
42025     {
42026         if(w !== null){
42027             this.el.setWidth(w);
42028             w -= this.el.getBorderWidth("rl");
42029             if(this.config.adjustments){
42030                 w += this.config.adjustments[0];
42031             }
42032         }
42033         if(h !== null && h > 0){
42034             this.el.setHeight(h);
42035             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42036             h -= this.el.getBorderWidth("tb");
42037             if(this.config.adjustments){
42038                 h += this.config.adjustments[1];
42039             }
42040             this.bodyEl.setHeight(h);
42041             if(this.tabs){
42042                 h = this.tabs.syncHeight(h);
42043             }
42044         }
42045         if(this.panelSize){
42046             w = w !== null ? w : this.panelSize.width;
42047             h = h !== null ? h : this.panelSize.height;
42048         }
42049         if(this.activePanel){
42050             var el = this.activePanel.getEl();
42051             w = w !== null ? w : el.getWidth();
42052             h = h !== null ? h : el.getHeight();
42053             this.panelSize = {width: w, height: h};
42054             this.activePanel.setSize(w, h);
42055         }
42056         if(Roo.isIE && this.tabs){
42057             this.tabs.el.repaint();
42058         }
42059     },
42060
42061     /**
42062      * Returns the container element for this region.
42063      * @return {Roo.Element}
42064      */
42065     getEl : function(){
42066         return this.el;
42067     },
42068
42069     /**
42070      * Hides this region.
42071      */
42072     hide : function(){
42073         //if(!this.collapsed){
42074             this.el.dom.style.left = "-2000px";
42075             this.el.hide();
42076         //}else{
42077          //   this.collapsedEl.dom.style.left = "-2000px";
42078          //   this.collapsedEl.hide();
42079        // }
42080         this.visible = false;
42081         this.fireEvent("visibilitychange", this, false);
42082     },
42083
42084     /**
42085      * Shows this region if it was previously hidden.
42086      */
42087     show : function(){
42088         //if(!this.collapsed){
42089             this.el.show();
42090         //}else{
42091         //    this.collapsedEl.show();
42092        // }
42093         this.visible = true;
42094         this.fireEvent("visibilitychange", this, true);
42095     },
42096 /*
42097     closeClicked : function(){
42098         if(this.activePanel){
42099             this.remove(this.activePanel);
42100         }
42101     },
42102
42103     collapseClick : function(e){
42104         if(this.isSlid){
42105            e.stopPropagation();
42106            this.slideIn();
42107         }else{
42108            e.stopPropagation();
42109            this.slideOut();
42110         }
42111     },
42112 */
42113     /**
42114      * Collapses this region.
42115      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42116      */
42117     /*
42118     collapse : function(skipAnim, skipCheck = false){
42119         if(this.collapsed) {
42120             return;
42121         }
42122         
42123         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42124             
42125             this.collapsed = true;
42126             if(this.split){
42127                 this.split.el.hide();
42128             }
42129             if(this.config.animate && skipAnim !== true){
42130                 this.fireEvent("invalidated", this);
42131                 this.animateCollapse();
42132             }else{
42133                 this.el.setLocation(-20000,-20000);
42134                 this.el.hide();
42135                 this.collapsedEl.show();
42136                 this.fireEvent("collapsed", this);
42137                 this.fireEvent("invalidated", this);
42138             }
42139         }
42140         
42141     },
42142 */
42143     animateCollapse : function(){
42144         // overridden
42145     },
42146
42147     /**
42148      * Expands this region if it was previously collapsed.
42149      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42150      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42151      */
42152     /*
42153     expand : function(e, skipAnim){
42154         if(e) {
42155             e.stopPropagation();
42156         }
42157         if(!this.collapsed || this.el.hasActiveFx()) {
42158             return;
42159         }
42160         if(this.isSlid){
42161             this.afterSlideIn();
42162             skipAnim = true;
42163         }
42164         this.collapsed = false;
42165         if(this.config.animate && skipAnim !== true){
42166             this.animateExpand();
42167         }else{
42168             this.el.show();
42169             if(this.split){
42170                 this.split.el.show();
42171             }
42172             this.collapsedEl.setLocation(-2000,-2000);
42173             this.collapsedEl.hide();
42174             this.fireEvent("invalidated", this);
42175             this.fireEvent("expanded", this);
42176         }
42177     },
42178 */
42179     animateExpand : function(){
42180         // overridden
42181     },
42182
42183     initTabs : function()
42184     {
42185         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42186         
42187         var ts = new Roo.bootstrap.panel.Tabs({
42188             el: this.bodyEl.dom,
42189             region : this,
42190             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42191             disableTooltips: this.config.disableTabTips,
42192             toolbar : this.config.toolbar
42193         });
42194         
42195         if(this.config.hideTabs){
42196             ts.stripWrap.setDisplayed(false);
42197         }
42198         this.tabs = ts;
42199         ts.resizeTabs = this.config.resizeTabs === true;
42200         ts.minTabWidth = this.config.minTabWidth || 40;
42201         ts.maxTabWidth = this.config.maxTabWidth || 250;
42202         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42203         ts.monitorResize = false;
42204         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42205         ts.bodyEl.addClass('roo-layout-tabs-body');
42206         this.panels.each(this.initPanelAsTab, this);
42207     },
42208
42209     initPanelAsTab : function(panel){
42210         var ti = this.tabs.addTab(
42211             panel.getEl().id,
42212             panel.getTitle(),
42213             null,
42214             this.config.closeOnTab && panel.isClosable(),
42215             panel.tpl
42216         );
42217         if(panel.tabTip !== undefined){
42218             ti.setTooltip(panel.tabTip);
42219         }
42220         ti.on("activate", function(){
42221               this.setActivePanel(panel);
42222         }, this);
42223         
42224         if(this.config.closeOnTab){
42225             ti.on("beforeclose", function(t, e){
42226                 e.cancel = true;
42227                 this.remove(panel);
42228             }, this);
42229         }
42230         
42231         panel.tabItem = ti;
42232         
42233         return ti;
42234     },
42235
42236     updatePanelTitle : function(panel, title)
42237     {
42238         if(this.activePanel == panel){
42239             this.updateTitle(title);
42240         }
42241         if(this.tabs){
42242             var ti = this.tabs.getTab(panel.getEl().id);
42243             ti.setText(title);
42244             if(panel.tabTip !== undefined){
42245                 ti.setTooltip(panel.tabTip);
42246             }
42247         }
42248     },
42249
42250     updateTitle : function(title){
42251         if(this.titleTextEl && !this.config.title){
42252             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42253         }
42254     },
42255
42256     setActivePanel : function(panel)
42257     {
42258         panel = this.getPanel(panel);
42259         if(this.activePanel && this.activePanel != panel){
42260             if(this.activePanel.setActiveState(false) === false){
42261                 return;
42262             }
42263         }
42264         this.activePanel = panel;
42265         panel.setActiveState(true);
42266         if(this.panelSize){
42267             panel.setSize(this.panelSize.width, this.panelSize.height);
42268         }
42269         if(this.closeBtn){
42270             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42271         }
42272         this.updateTitle(panel.getTitle());
42273         if(this.tabs){
42274             this.fireEvent("invalidated", this);
42275         }
42276         this.fireEvent("panelactivated", this, panel);
42277     },
42278
42279     /**
42280      * Shows the specified panel.
42281      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42282      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42283      */
42284     showPanel : function(panel)
42285     {
42286         panel = this.getPanel(panel);
42287         if(panel){
42288             if(this.tabs){
42289                 var tab = this.tabs.getTab(panel.getEl().id);
42290                 if(tab.isHidden()){
42291                     this.tabs.unhideTab(tab.id);
42292                 }
42293                 tab.activate();
42294             }else{
42295                 this.setActivePanel(panel);
42296             }
42297         }
42298         return panel;
42299     },
42300
42301     /**
42302      * Get the active panel for this region.
42303      * @return {Roo.ContentPanel} The active panel or null
42304      */
42305     getActivePanel : function(){
42306         return this.activePanel;
42307     },
42308
42309     validateVisibility : function(){
42310         if(this.panels.getCount() < 1){
42311             this.updateTitle("&#160;");
42312             this.closeBtn.hide();
42313             this.hide();
42314         }else{
42315             if(!this.isVisible()){
42316                 this.show();
42317             }
42318         }
42319     },
42320
42321     /**
42322      * Adds the passed ContentPanel(s) to this region.
42323      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42324      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42325      */
42326     add : function(panel)
42327     {
42328         if(arguments.length > 1){
42329             for(var i = 0, len = arguments.length; i < len; i++) {
42330                 this.add(arguments[i]);
42331             }
42332             return null;
42333         }
42334         
42335         // if we have not been rendered yet, then we can not really do much of this..
42336         if (!this.bodyEl) {
42337             this.unrendered_panels.push(panel);
42338             return panel;
42339         }
42340         
42341         
42342         
42343         
42344         if(this.hasPanel(panel)){
42345             this.showPanel(panel);
42346             return panel;
42347         }
42348         panel.setRegion(this);
42349         this.panels.add(panel);
42350        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42351             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42352             // and hide them... ???
42353             this.bodyEl.dom.appendChild(panel.getEl().dom);
42354             if(panel.background !== true){
42355                 this.setActivePanel(panel);
42356             }
42357             this.fireEvent("paneladded", this, panel);
42358             return panel;
42359         }
42360         */
42361         if(!this.tabs){
42362             this.initTabs();
42363         }else{
42364             this.initPanelAsTab(panel);
42365         }
42366         
42367         
42368         if(panel.background !== true){
42369             this.tabs.activate(panel.getEl().id);
42370         }
42371         this.fireEvent("paneladded", this, panel);
42372         return panel;
42373     },
42374
42375     /**
42376      * Hides the tab for the specified panel.
42377      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42378      */
42379     hidePanel : function(panel){
42380         if(this.tabs && (panel = this.getPanel(panel))){
42381             this.tabs.hideTab(panel.getEl().id);
42382         }
42383     },
42384
42385     /**
42386      * Unhides the tab for a previously hidden panel.
42387      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42388      */
42389     unhidePanel : function(panel){
42390         if(this.tabs && (panel = this.getPanel(panel))){
42391             this.tabs.unhideTab(panel.getEl().id);
42392         }
42393     },
42394
42395     clearPanels : function(){
42396         while(this.panels.getCount() > 0){
42397              this.remove(this.panels.first());
42398         }
42399     },
42400
42401     /**
42402      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42403      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42404      * @param {Boolean} preservePanel Overrides the config preservePanel option
42405      * @return {Roo.ContentPanel} The panel that was removed
42406      */
42407     remove : function(panel, preservePanel)
42408     {
42409         panel = this.getPanel(panel);
42410         if(!panel){
42411             return null;
42412         }
42413         var e = {};
42414         this.fireEvent("beforeremove", this, panel, e);
42415         if(e.cancel === true){
42416             return null;
42417         }
42418         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42419         var panelId = panel.getId();
42420         this.panels.removeKey(panelId);
42421         if(preservePanel){
42422             document.body.appendChild(panel.getEl().dom);
42423         }
42424         if(this.tabs){
42425             this.tabs.removeTab(panel.getEl().id);
42426         }else if (!preservePanel){
42427             this.bodyEl.dom.removeChild(panel.getEl().dom);
42428         }
42429         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42430             var p = this.panels.first();
42431             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42432             tempEl.appendChild(p.getEl().dom);
42433             this.bodyEl.update("");
42434             this.bodyEl.dom.appendChild(p.getEl().dom);
42435             tempEl = null;
42436             this.updateTitle(p.getTitle());
42437             this.tabs = null;
42438             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42439             this.setActivePanel(p);
42440         }
42441         panel.setRegion(null);
42442         if(this.activePanel == panel){
42443             this.activePanel = null;
42444         }
42445         if(this.config.autoDestroy !== false && preservePanel !== true){
42446             try{panel.destroy();}catch(e){}
42447         }
42448         this.fireEvent("panelremoved", this, panel);
42449         return panel;
42450     },
42451
42452     /**
42453      * Returns the TabPanel component used by this region
42454      * @return {Roo.TabPanel}
42455      */
42456     getTabs : function(){
42457         return this.tabs;
42458     },
42459
42460     createTool : function(parentEl, className){
42461         var btn = Roo.DomHelper.append(parentEl, {
42462             tag: "div",
42463             cls: "x-layout-tools-button",
42464             children: [ {
42465                 tag: "div",
42466                 cls: "roo-layout-tools-button-inner " + className,
42467                 html: "&#160;"
42468             }]
42469         }, true);
42470         btn.addClassOnOver("roo-layout-tools-button-over");
42471         return btn;
42472     }
42473 });/*
42474  * Based on:
42475  * Ext JS Library 1.1.1
42476  * Copyright(c) 2006-2007, Ext JS, LLC.
42477  *
42478  * Originally Released Under LGPL - original licence link has changed is not relivant.
42479  *
42480  * Fork - LGPL
42481  * <script type="text/javascript">
42482  */
42483  
42484
42485
42486 /**
42487  * @class Roo.SplitLayoutRegion
42488  * @extends Roo.LayoutRegion
42489  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42490  */
42491 Roo.bootstrap.layout.Split = function(config){
42492     this.cursor = config.cursor;
42493     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42494 };
42495
42496 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42497 {
42498     splitTip : "Drag to resize.",
42499     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42500     useSplitTips : false,
42501
42502     applyConfig : function(config){
42503         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42504     },
42505     
42506     onRender : function(ctr,pos) {
42507         
42508         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42509         if(!this.config.split){
42510             return;
42511         }
42512         if(!this.split){
42513             
42514             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42515                             tag: "div",
42516                             id: this.el.id + "-split",
42517                             cls: "roo-layout-split roo-layout-split-"+this.position,
42518                             html: "&#160;"
42519             });
42520             /** The SplitBar for this region 
42521             * @type Roo.SplitBar */
42522             // does not exist yet...
42523             Roo.log([this.position, this.orientation]);
42524             
42525             this.split = new Roo.bootstrap.SplitBar({
42526                 dragElement : splitEl,
42527                 resizingElement: this.el,
42528                 orientation : this.orientation
42529             });
42530             
42531             this.split.on("moved", this.onSplitMove, this);
42532             this.split.useShim = this.config.useShim === true;
42533             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42534             if(this.useSplitTips){
42535                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42536             }
42537             //if(config.collapsible){
42538             //    this.split.el.on("dblclick", this.collapse,  this);
42539             //}
42540         }
42541         if(typeof this.config.minSize != "undefined"){
42542             this.split.minSize = this.config.minSize;
42543         }
42544         if(typeof this.config.maxSize != "undefined"){
42545             this.split.maxSize = this.config.maxSize;
42546         }
42547         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42548             this.hideSplitter();
42549         }
42550         
42551     },
42552
42553     getHMaxSize : function(){
42554          var cmax = this.config.maxSize || 10000;
42555          var center = this.mgr.getRegion("center");
42556          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42557     },
42558
42559     getVMaxSize : function(){
42560          var cmax = this.config.maxSize || 10000;
42561          var center = this.mgr.getRegion("center");
42562          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42563     },
42564
42565     onSplitMove : function(split, newSize){
42566         this.fireEvent("resized", this, newSize);
42567     },
42568     
42569     /** 
42570      * Returns the {@link Roo.SplitBar} for this region.
42571      * @return {Roo.SplitBar}
42572      */
42573     getSplitBar : function(){
42574         return this.split;
42575     },
42576     
42577     hide : function(){
42578         this.hideSplitter();
42579         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42580     },
42581
42582     hideSplitter : function(){
42583         if(this.split){
42584             this.split.el.setLocation(-2000,-2000);
42585             this.split.el.hide();
42586         }
42587     },
42588
42589     show : function(){
42590         if(this.split){
42591             this.split.el.show();
42592         }
42593         Roo.bootstrap.layout.Split.superclass.show.call(this);
42594     },
42595     
42596     beforeSlide: function(){
42597         if(Roo.isGecko){// firefox overflow auto bug workaround
42598             this.bodyEl.clip();
42599             if(this.tabs) {
42600                 this.tabs.bodyEl.clip();
42601             }
42602             if(this.activePanel){
42603                 this.activePanel.getEl().clip();
42604                 
42605                 if(this.activePanel.beforeSlide){
42606                     this.activePanel.beforeSlide();
42607                 }
42608             }
42609         }
42610     },
42611     
42612     afterSlide : function(){
42613         if(Roo.isGecko){// firefox overflow auto bug workaround
42614             this.bodyEl.unclip();
42615             if(this.tabs) {
42616                 this.tabs.bodyEl.unclip();
42617             }
42618             if(this.activePanel){
42619                 this.activePanel.getEl().unclip();
42620                 if(this.activePanel.afterSlide){
42621                     this.activePanel.afterSlide();
42622                 }
42623             }
42624         }
42625     },
42626
42627     initAutoHide : function(){
42628         if(this.autoHide !== false){
42629             if(!this.autoHideHd){
42630                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42631                 this.autoHideHd = {
42632                     "mouseout": function(e){
42633                         if(!e.within(this.el, true)){
42634                             st.delay(500);
42635                         }
42636                     },
42637                     "mouseover" : function(e){
42638                         st.cancel();
42639                     },
42640                     scope : this
42641                 };
42642             }
42643             this.el.on(this.autoHideHd);
42644         }
42645     },
42646
42647     clearAutoHide : function(){
42648         if(this.autoHide !== false){
42649             this.el.un("mouseout", this.autoHideHd.mouseout);
42650             this.el.un("mouseover", this.autoHideHd.mouseover);
42651         }
42652     },
42653
42654     clearMonitor : function(){
42655         Roo.get(document).un("click", this.slideInIf, this);
42656     },
42657
42658     // these names are backwards but not changed for compat
42659     slideOut : function(){
42660         if(this.isSlid || this.el.hasActiveFx()){
42661             return;
42662         }
42663         this.isSlid = true;
42664         if(this.collapseBtn){
42665             this.collapseBtn.hide();
42666         }
42667         this.closeBtnState = this.closeBtn.getStyle('display');
42668         this.closeBtn.hide();
42669         if(this.stickBtn){
42670             this.stickBtn.show();
42671         }
42672         this.el.show();
42673         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42674         this.beforeSlide();
42675         this.el.setStyle("z-index", 10001);
42676         this.el.slideIn(this.getSlideAnchor(), {
42677             callback: function(){
42678                 this.afterSlide();
42679                 this.initAutoHide();
42680                 Roo.get(document).on("click", this.slideInIf, this);
42681                 this.fireEvent("slideshow", this);
42682             },
42683             scope: this,
42684             block: true
42685         });
42686     },
42687
42688     afterSlideIn : function(){
42689         this.clearAutoHide();
42690         this.isSlid = false;
42691         this.clearMonitor();
42692         this.el.setStyle("z-index", "");
42693         if(this.collapseBtn){
42694             this.collapseBtn.show();
42695         }
42696         this.closeBtn.setStyle('display', this.closeBtnState);
42697         if(this.stickBtn){
42698             this.stickBtn.hide();
42699         }
42700         this.fireEvent("slidehide", this);
42701     },
42702
42703     slideIn : function(cb){
42704         if(!this.isSlid || this.el.hasActiveFx()){
42705             Roo.callback(cb);
42706             return;
42707         }
42708         this.isSlid = false;
42709         this.beforeSlide();
42710         this.el.slideOut(this.getSlideAnchor(), {
42711             callback: function(){
42712                 this.el.setLeftTop(-10000, -10000);
42713                 this.afterSlide();
42714                 this.afterSlideIn();
42715                 Roo.callback(cb);
42716             },
42717             scope: this,
42718             block: true
42719         });
42720     },
42721     
42722     slideInIf : function(e){
42723         if(!e.within(this.el)){
42724             this.slideIn();
42725         }
42726     },
42727
42728     animateCollapse : function(){
42729         this.beforeSlide();
42730         this.el.setStyle("z-index", 20000);
42731         var anchor = this.getSlideAnchor();
42732         this.el.slideOut(anchor, {
42733             callback : function(){
42734                 this.el.setStyle("z-index", "");
42735                 this.collapsedEl.slideIn(anchor, {duration:.3});
42736                 this.afterSlide();
42737                 this.el.setLocation(-10000,-10000);
42738                 this.el.hide();
42739                 this.fireEvent("collapsed", this);
42740             },
42741             scope: this,
42742             block: true
42743         });
42744     },
42745
42746     animateExpand : function(){
42747         this.beforeSlide();
42748         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42749         this.el.setStyle("z-index", 20000);
42750         this.collapsedEl.hide({
42751             duration:.1
42752         });
42753         this.el.slideIn(this.getSlideAnchor(), {
42754             callback : function(){
42755                 this.el.setStyle("z-index", "");
42756                 this.afterSlide();
42757                 if(this.split){
42758                     this.split.el.show();
42759                 }
42760                 this.fireEvent("invalidated", this);
42761                 this.fireEvent("expanded", this);
42762             },
42763             scope: this,
42764             block: true
42765         });
42766     },
42767
42768     anchors : {
42769         "west" : "left",
42770         "east" : "right",
42771         "north" : "top",
42772         "south" : "bottom"
42773     },
42774
42775     sanchors : {
42776         "west" : "l",
42777         "east" : "r",
42778         "north" : "t",
42779         "south" : "b"
42780     },
42781
42782     canchors : {
42783         "west" : "tl-tr",
42784         "east" : "tr-tl",
42785         "north" : "tl-bl",
42786         "south" : "bl-tl"
42787     },
42788
42789     getAnchor : function(){
42790         return this.anchors[this.position];
42791     },
42792
42793     getCollapseAnchor : function(){
42794         return this.canchors[this.position];
42795     },
42796
42797     getSlideAnchor : function(){
42798         return this.sanchors[this.position];
42799     },
42800
42801     getAlignAdj : function(){
42802         var cm = this.cmargins;
42803         switch(this.position){
42804             case "west":
42805                 return [0, 0];
42806             break;
42807             case "east":
42808                 return [0, 0];
42809             break;
42810             case "north":
42811                 return [0, 0];
42812             break;
42813             case "south":
42814                 return [0, 0];
42815             break;
42816         }
42817     },
42818
42819     getExpandAdj : function(){
42820         var c = this.collapsedEl, cm = this.cmargins;
42821         switch(this.position){
42822             case "west":
42823                 return [-(cm.right+c.getWidth()+cm.left), 0];
42824             break;
42825             case "east":
42826                 return [cm.right+c.getWidth()+cm.left, 0];
42827             break;
42828             case "north":
42829                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42830             break;
42831             case "south":
42832                 return [0, cm.top+cm.bottom+c.getHeight()];
42833             break;
42834         }
42835     }
42836 });/*
42837  * Based on:
42838  * Ext JS Library 1.1.1
42839  * Copyright(c) 2006-2007, Ext JS, LLC.
42840  *
42841  * Originally Released Under LGPL - original licence link has changed is not relivant.
42842  *
42843  * Fork - LGPL
42844  * <script type="text/javascript">
42845  */
42846 /*
42847  * These classes are private internal classes
42848  */
42849 Roo.bootstrap.layout.Center = function(config){
42850     config.region = "center";
42851     Roo.bootstrap.layout.Region.call(this, config);
42852     this.visible = true;
42853     this.minWidth = config.minWidth || 20;
42854     this.minHeight = config.minHeight || 20;
42855 };
42856
42857 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42858     hide : function(){
42859         // center panel can't be hidden
42860     },
42861     
42862     show : function(){
42863         // center panel can't be hidden
42864     },
42865     
42866     getMinWidth: function(){
42867         return this.minWidth;
42868     },
42869     
42870     getMinHeight: function(){
42871         return this.minHeight;
42872     }
42873 });
42874
42875
42876
42877
42878  
42879
42880
42881
42882
42883
42884
42885 Roo.bootstrap.layout.North = function(config)
42886 {
42887     config.region = 'north';
42888     config.cursor = 'n-resize';
42889     
42890     Roo.bootstrap.layout.Split.call(this, config);
42891     
42892     
42893     if(this.split){
42894         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42895         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42896         this.split.el.addClass("roo-layout-split-v");
42897     }
42898     //var size = config.initialSize || config.height;
42899     //if(this.el && typeof size != "undefined"){
42900     //    this.el.setHeight(size);
42901     //}
42902 };
42903 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42904 {
42905     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42906      
42907      
42908     onRender : function(ctr, pos)
42909     {
42910         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42911         var size = this.config.initialSize || this.config.height;
42912         if(this.el && typeof size != "undefined"){
42913             this.el.setHeight(size);
42914         }
42915     
42916     },
42917     
42918     getBox : function(){
42919         if(this.collapsed){
42920             return this.collapsedEl.getBox();
42921         }
42922         var box = this.el.getBox();
42923         if(this.split){
42924             box.height += this.split.el.getHeight();
42925         }
42926         return box;
42927     },
42928     
42929     updateBox : function(box){
42930         if(this.split && !this.collapsed){
42931             box.height -= this.split.el.getHeight();
42932             this.split.el.setLeft(box.x);
42933             this.split.el.setTop(box.y+box.height);
42934             this.split.el.setWidth(box.width);
42935         }
42936         if(this.collapsed){
42937             this.updateBody(box.width, null);
42938         }
42939         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42940     }
42941 });
42942
42943
42944
42945
42946
42947 Roo.bootstrap.layout.South = function(config){
42948     config.region = 'south';
42949     config.cursor = 's-resize';
42950     Roo.bootstrap.layout.Split.call(this, config);
42951     if(this.split){
42952         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42953         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42954         this.split.el.addClass("roo-layout-split-v");
42955     }
42956     
42957 };
42958
42959 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42960     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42961     
42962     onRender : function(ctr, pos)
42963     {
42964         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42965         var size = this.config.initialSize || this.config.height;
42966         if(this.el && typeof size != "undefined"){
42967             this.el.setHeight(size);
42968         }
42969     
42970     },
42971     
42972     getBox : function(){
42973         if(this.collapsed){
42974             return this.collapsedEl.getBox();
42975         }
42976         var box = this.el.getBox();
42977         if(this.split){
42978             var sh = this.split.el.getHeight();
42979             box.height += sh;
42980             box.y -= sh;
42981         }
42982         return box;
42983     },
42984     
42985     updateBox : function(box){
42986         if(this.split && !this.collapsed){
42987             var sh = this.split.el.getHeight();
42988             box.height -= sh;
42989             box.y += sh;
42990             this.split.el.setLeft(box.x);
42991             this.split.el.setTop(box.y-sh);
42992             this.split.el.setWidth(box.width);
42993         }
42994         if(this.collapsed){
42995             this.updateBody(box.width, null);
42996         }
42997         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42998     }
42999 });
43000
43001 Roo.bootstrap.layout.East = function(config){
43002     config.region = "east";
43003     config.cursor = "e-resize";
43004     Roo.bootstrap.layout.Split.call(this, config);
43005     if(this.split){
43006         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
43007         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43008         this.split.el.addClass("roo-layout-split-h");
43009     }
43010     
43011 };
43012 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43013     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43014     
43015     onRender : function(ctr, pos)
43016     {
43017         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43018         var size = this.config.initialSize || this.config.width;
43019         if(this.el && typeof size != "undefined"){
43020             this.el.setWidth(size);
43021         }
43022     
43023     },
43024     
43025     getBox : function(){
43026         if(this.collapsed){
43027             return this.collapsedEl.getBox();
43028         }
43029         var box = this.el.getBox();
43030         if(this.split){
43031             var sw = this.split.el.getWidth();
43032             box.width += sw;
43033             box.x -= sw;
43034         }
43035         return box;
43036     },
43037
43038     updateBox : function(box){
43039         if(this.split && !this.collapsed){
43040             var sw = this.split.el.getWidth();
43041             box.width -= sw;
43042             this.split.el.setLeft(box.x);
43043             this.split.el.setTop(box.y);
43044             this.split.el.setHeight(box.height);
43045             box.x += sw;
43046         }
43047         if(this.collapsed){
43048             this.updateBody(null, box.height);
43049         }
43050         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43051     }
43052 });
43053
43054 Roo.bootstrap.layout.West = function(config){
43055     config.region = "west";
43056     config.cursor = "w-resize";
43057     
43058     Roo.bootstrap.layout.Split.call(this, config);
43059     if(this.split){
43060         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43061         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43062         this.split.el.addClass("roo-layout-split-h");
43063     }
43064     
43065 };
43066 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43067     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43068     
43069     onRender: function(ctr, pos)
43070     {
43071         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43072         var size = this.config.initialSize || this.config.width;
43073         if(typeof size != "undefined"){
43074             this.el.setWidth(size);
43075         }
43076     },
43077     
43078     getBox : function(){
43079         if(this.collapsed){
43080             return this.collapsedEl.getBox();
43081         }
43082         var box = this.el.getBox();
43083         if (box.width == 0) {
43084             box.width = this.config.width; // kludge?
43085         }
43086         if(this.split){
43087             box.width += this.split.el.getWidth();
43088         }
43089         return box;
43090     },
43091     
43092     updateBox : function(box){
43093         if(this.split && !this.collapsed){
43094             var sw = this.split.el.getWidth();
43095             box.width -= sw;
43096             this.split.el.setLeft(box.x+box.width);
43097             this.split.el.setTop(box.y);
43098             this.split.el.setHeight(box.height);
43099         }
43100         if(this.collapsed){
43101             this.updateBody(null, box.height);
43102         }
43103         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43104     }
43105 });/*
43106  * Based on:
43107  * Ext JS Library 1.1.1
43108  * Copyright(c) 2006-2007, Ext JS, LLC.
43109  *
43110  * Originally Released Under LGPL - original licence link has changed is not relivant.
43111  *
43112  * Fork - LGPL
43113  * <script type="text/javascript">
43114  */
43115 /**
43116  * @class Roo.bootstrap.paenl.Content
43117  * @extends Roo.util.Observable
43118  * @children Roo.bootstrap.Component
43119  * @parent builder Roo.bootstrap.layout.Border
43120  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43121  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43122  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43123  * @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
43124  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43125  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43126  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43127  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43128  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43129  * @cfg {String} title          The title for this panel
43130  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43131  * @cfg {String} url            Calls {@link #setUrl} with this value
43132  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43133  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43134  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43135  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43136  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43137  * @cfg {Boolean} badges render the badges
43138  * @cfg {String} cls  extra classes to use  
43139  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43140  
43141  * @constructor
43142  * Create a new ContentPanel.
43143  * @param {String/Object} config A string to set only the title or a config object
43144  
43145  */
43146 Roo.bootstrap.panel.Content = function( config){
43147     
43148     this.tpl = config.tpl || false;
43149     
43150     var el = config.el;
43151     var content = config.content;
43152
43153     if(config.autoCreate){ // xtype is available if this is called from factory
43154         el = Roo.id();
43155     }
43156     this.el = Roo.get(el);
43157     if(!this.el && config && config.autoCreate){
43158         if(typeof config.autoCreate == "object"){
43159             if(!config.autoCreate.id){
43160                 config.autoCreate.id = config.id||el;
43161             }
43162             this.el = Roo.DomHelper.append(document.body,
43163                         config.autoCreate, true);
43164         }else{
43165             var elcfg =  {
43166                 tag: "div",
43167                 cls: (config.cls || '') +
43168                     (config.background ? ' bg-' + config.background : '') +
43169                     " roo-layout-inactive-content",
43170                 id: config.id||el
43171             };
43172             if (config.iframe) {
43173                 elcfg.cn = [
43174                     {
43175                         tag : 'iframe',
43176                         style : 'border: 0px',
43177                         src : 'about:blank'
43178                     }
43179                 ];
43180             }
43181               
43182             if (config.html) {
43183                 elcfg.html = config.html;
43184                 
43185             }
43186                         
43187             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43188             if (config.iframe) {
43189                 this.iframeEl = this.el.select('iframe',true).first();
43190             }
43191             
43192         }
43193     } 
43194     this.closable = false;
43195     this.loaded = false;
43196     this.active = false;
43197    
43198       
43199     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43200         
43201         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43202         
43203         this.wrapEl = this.el; //this.el.wrap();
43204         var ti = [];
43205         if (config.toolbar.items) {
43206             ti = config.toolbar.items ;
43207             delete config.toolbar.items ;
43208         }
43209         
43210         var nitems = [];
43211         this.toolbar.render(this.wrapEl, 'before');
43212         for(var i =0;i < ti.length;i++) {
43213           //  Roo.log(['add child', items[i]]);
43214             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43215         }
43216         this.toolbar.items = nitems;
43217         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43218         delete config.toolbar;
43219         
43220     }
43221     /*
43222     // xtype created footer. - not sure if will work as we normally have to render first..
43223     if (this.footer && !this.footer.el && this.footer.xtype) {
43224         if (!this.wrapEl) {
43225             this.wrapEl = this.el.wrap();
43226         }
43227     
43228         this.footer.container = this.wrapEl.createChild();
43229          
43230         this.footer = Roo.factory(this.footer, Roo);
43231         
43232     }
43233     */
43234     
43235      if(typeof config == "string"){
43236         this.title = config;
43237     }else{
43238         Roo.apply(this, config);
43239     }
43240     
43241     if(this.resizeEl){
43242         this.resizeEl = Roo.get(this.resizeEl, true);
43243     }else{
43244         this.resizeEl = this.el;
43245     }
43246     // handle view.xtype
43247     
43248  
43249     
43250     
43251     this.addEvents({
43252         /**
43253          * @event activate
43254          * Fires when this panel is activated. 
43255          * @param {Roo.ContentPanel} this
43256          */
43257         "activate" : true,
43258         /**
43259          * @event deactivate
43260          * Fires when this panel is activated. 
43261          * @param {Roo.ContentPanel} this
43262          */
43263         "deactivate" : true,
43264
43265         /**
43266          * @event resize
43267          * Fires when this panel is resized if fitToFrame is true.
43268          * @param {Roo.ContentPanel} this
43269          * @param {Number} width The width after any component adjustments
43270          * @param {Number} height The height after any component adjustments
43271          */
43272         "resize" : true,
43273         
43274          /**
43275          * @event render
43276          * Fires when this tab is created
43277          * @param {Roo.ContentPanel} this
43278          */
43279         "render" : true,
43280         
43281           /**
43282          * @event scroll
43283          * Fires when this content is scrolled
43284          * @param {Roo.ContentPanel} this
43285          * @param {Event} scrollEvent
43286          */
43287         "scroll" : true
43288         
43289         
43290         
43291     });
43292     
43293
43294     
43295     
43296     if(this.autoScroll && !this.iframe){
43297         this.resizeEl.setStyle("overflow", "auto");
43298         this.resizeEl.on('scroll', this.onScroll, this);
43299     } else {
43300         // fix randome scrolling
43301         //this.el.on('scroll', function() {
43302         //    Roo.log('fix random scolling');
43303         //    this.scrollTo('top',0); 
43304         //});
43305     }
43306     content = content || this.content;
43307     if(content){
43308         this.setContent(content);
43309     }
43310     if(config && config.url){
43311         this.setUrl(this.url, this.params, this.loadOnce);
43312     }
43313     
43314     
43315     
43316     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43317     
43318     if (this.view && typeof(this.view.xtype) != 'undefined') {
43319         this.view.el = this.el.appendChild(document.createElement("div"));
43320         this.view = Roo.factory(this.view); 
43321         this.view.render  &&  this.view.render(false, '');  
43322     }
43323     
43324     
43325     this.fireEvent('render', this);
43326 };
43327
43328 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43329     
43330     cls : '',
43331     background : '',
43332     
43333     tabTip : '',
43334     
43335     iframe : false,
43336     iframeEl : false,
43337     
43338     /* Resize Element - use this to work out scroll etc. */
43339     resizeEl : false,
43340     
43341     setRegion : function(region){
43342         this.region = region;
43343         this.setActiveClass(region && !this.background);
43344     },
43345     
43346     
43347     setActiveClass: function(state)
43348     {
43349         if(state){
43350            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43351            this.el.setStyle('position','relative');
43352         }else{
43353            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43354            this.el.setStyle('position', 'absolute');
43355         } 
43356     },
43357     
43358     /**
43359      * Returns the toolbar for this Panel if one was configured. 
43360      * @return {Roo.Toolbar} 
43361      */
43362     getToolbar : function(){
43363         return this.toolbar;
43364     },
43365     
43366     setActiveState : function(active)
43367     {
43368         this.active = active;
43369         this.setActiveClass(active);
43370         if(!active){
43371             if(this.fireEvent("deactivate", this) === false){
43372                 return false;
43373             }
43374             return true;
43375         }
43376         this.fireEvent("activate", this);
43377         return true;
43378     },
43379     /**
43380      * Updates this panel's element (not for iframe)
43381      * @param {String} content The new content
43382      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43383     */
43384     setContent : function(content, loadScripts){
43385         if (this.iframe) {
43386             return;
43387         }
43388         
43389         this.el.update(content, loadScripts);
43390     },
43391
43392     ignoreResize : function(w, h)
43393     {
43394         //return false; // always resize?
43395         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43396             return true;
43397         }else{
43398             this.lastSize = {width: w, height: h};
43399             return false;
43400         }
43401     },
43402     /**
43403      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43404      * @return {Roo.UpdateManager} The UpdateManager
43405      */
43406     getUpdateManager : function(){
43407         if (this.iframe) {
43408             return false;
43409         }
43410         return this.el.getUpdateManager();
43411     },
43412      /**
43413      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43414      * Does not work with IFRAME contents
43415      * @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:
43416 <pre><code>
43417 panel.load({
43418     url: "your-url.php",
43419     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43420     callback: yourFunction,
43421     scope: yourObject, //(optional scope)
43422     discardUrl: false,
43423     nocache: false,
43424     text: "Loading...",
43425     timeout: 30,
43426     scripts: false
43427 });
43428 </code></pre>
43429      
43430      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43431      * 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.
43432      * @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}
43433      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43434      * @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.
43435      * @return {Roo.ContentPanel} this
43436      */
43437     load : function(){
43438         
43439         if (this.iframe) {
43440             return this;
43441         }
43442         
43443         var um = this.el.getUpdateManager();
43444         um.update.apply(um, arguments);
43445         return this;
43446     },
43447
43448
43449     /**
43450      * 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.
43451      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43452      * @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)
43453      * @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)
43454      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43455      */
43456     setUrl : function(url, params, loadOnce){
43457         if (this.iframe) {
43458             this.iframeEl.dom.src = url;
43459             return false;
43460         }
43461         
43462         if(this.refreshDelegate){
43463             this.removeListener("activate", this.refreshDelegate);
43464         }
43465         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43466         this.on("activate", this.refreshDelegate);
43467         return this.el.getUpdateManager();
43468     },
43469     
43470     _handleRefresh : function(url, params, loadOnce){
43471         if(!loadOnce || !this.loaded){
43472             var updater = this.el.getUpdateManager();
43473             updater.update(url, params, this._setLoaded.createDelegate(this));
43474         }
43475     },
43476     
43477     _setLoaded : function(){
43478         this.loaded = true;
43479     }, 
43480     
43481     /**
43482      * Returns this panel's id
43483      * @return {String} 
43484      */
43485     getId : function(){
43486         return this.el.id;
43487     },
43488     
43489     /** 
43490      * Returns this panel's element - used by regiosn to add.
43491      * @return {Roo.Element} 
43492      */
43493     getEl : function(){
43494         return this.wrapEl || this.el;
43495     },
43496     
43497    
43498     
43499     adjustForComponents : function(width, height)
43500     {
43501         //Roo.log('adjustForComponents ');
43502         if(this.resizeEl != this.el){
43503             width -= this.el.getFrameWidth('lr');
43504             height -= this.el.getFrameWidth('tb');
43505         }
43506         if(this.toolbar){
43507             var te = this.toolbar.getEl();
43508             te.setWidth(width);
43509             height -= te.getHeight();
43510         }
43511         if(this.footer){
43512             var te = this.footer.getEl();
43513             te.setWidth(width);
43514             height -= te.getHeight();
43515         }
43516         
43517         
43518         if(this.adjustments){
43519             width += this.adjustments[0];
43520             height += this.adjustments[1];
43521         }
43522         return {"width": width, "height": height};
43523     },
43524     
43525     setSize : function(width, height){
43526         if(this.fitToFrame && !this.ignoreResize(width, height)){
43527             if(this.fitContainer && this.resizeEl != this.el){
43528                 this.el.setSize(width, height);
43529             }
43530             var size = this.adjustForComponents(width, height);
43531             if (this.iframe) {
43532                 this.iframeEl.setSize(width,height);
43533             }
43534             
43535             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43536             this.fireEvent('resize', this, size.width, size.height);
43537             
43538             
43539         }
43540     },
43541     
43542     /**
43543      * Returns this panel's title
43544      * @return {String} 
43545      */
43546     getTitle : function(){
43547         
43548         if (typeof(this.title) != 'object') {
43549             return this.title;
43550         }
43551         
43552         var t = '';
43553         for (var k in this.title) {
43554             if (!this.title.hasOwnProperty(k)) {
43555                 continue;
43556             }
43557             
43558             if (k.indexOf('-') >= 0) {
43559                 var s = k.split('-');
43560                 for (var i = 0; i<s.length; i++) {
43561                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43562                 }
43563             } else {
43564                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43565             }
43566         }
43567         return t;
43568     },
43569     
43570     /**
43571      * Set this panel's title
43572      * @param {String} title
43573      */
43574     setTitle : function(title){
43575         this.title = title;
43576         if(this.region){
43577             this.region.updatePanelTitle(this, title);
43578         }
43579     },
43580     
43581     /**
43582      * Returns true is this panel was configured to be closable
43583      * @return {Boolean} 
43584      */
43585     isClosable : function(){
43586         return this.closable;
43587     },
43588     
43589     beforeSlide : function(){
43590         this.el.clip();
43591         this.resizeEl.clip();
43592     },
43593     
43594     afterSlide : function(){
43595         this.el.unclip();
43596         this.resizeEl.unclip();
43597     },
43598     
43599     /**
43600      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43601      *   Will fail silently if the {@link #setUrl} method has not been called.
43602      *   This does not activate the panel, just updates its content.
43603      */
43604     refresh : function(){
43605         if(this.refreshDelegate){
43606            this.loaded = false;
43607            this.refreshDelegate();
43608         }
43609     },
43610     
43611     /**
43612      * Destroys this panel
43613      */
43614     destroy : function(){
43615         this.el.removeAllListeners();
43616         var tempEl = document.createElement("span");
43617         tempEl.appendChild(this.el.dom);
43618         tempEl.innerHTML = "";
43619         this.el.remove();
43620         this.el = null;
43621     },
43622     
43623     /**
43624      * form - if the content panel contains a form - this is a reference to it.
43625      * @type {Roo.form.Form}
43626      */
43627     form : false,
43628     /**
43629      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43630      *    This contains a reference to it.
43631      * @type {Roo.View}
43632      */
43633     view : false,
43634     
43635       /**
43636      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43637      * <pre><code>
43638
43639 layout.addxtype({
43640        xtype : 'Form',
43641        items: [ .... ]
43642    }
43643 );
43644
43645 </code></pre>
43646      * @param {Object} cfg Xtype definition of item to add.
43647      */
43648     
43649     
43650     getChildContainer: function () {
43651         return this.getEl();
43652     },
43653     
43654     
43655     onScroll : function(e)
43656     {
43657         this.fireEvent('scroll', this, e);
43658     }
43659     
43660     
43661     /*
43662         var  ret = new Roo.factory(cfg);
43663         return ret;
43664         
43665         
43666         // add form..
43667         if (cfg.xtype.match(/^Form$/)) {
43668             
43669             var el;
43670             //if (this.footer) {
43671             //    el = this.footer.container.insertSibling(false, 'before');
43672             //} else {
43673                 el = this.el.createChild();
43674             //}
43675
43676             this.form = new  Roo.form.Form(cfg);
43677             
43678             
43679             if ( this.form.allItems.length) {
43680                 this.form.render(el.dom);
43681             }
43682             return this.form;
43683         }
43684         // should only have one of theses..
43685         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43686             // views.. should not be just added - used named prop 'view''
43687             
43688             cfg.el = this.el.appendChild(document.createElement("div"));
43689             // factory?
43690             
43691             var ret = new Roo.factory(cfg);
43692              
43693              ret.render && ret.render(false, ''); // render blank..
43694             this.view = ret;
43695             return ret;
43696         }
43697         return false;
43698     }
43699     \*/
43700 });
43701  
43702 /**
43703  * @class Roo.bootstrap.panel.Grid
43704  * @extends Roo.bootstrap.panel.Content
43705  * @constructor
43706  * Create a new GridPanel.
43707  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43708  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43709  * @param {Object} config A the config object
43710   
43711  */
43712
43713
43714
43715 Roo.bootstrap.panel.Grid = function(config)
43716 {
43717     
43718       
43719     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43720         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43721
43722     config.el = this.wrapper;
43723     //this.el = this.wrapper;
43724     
43725       if (config.container) {
43726         // ctor'ed from a Border/panel.grid
43727         
43728         
43729         this.wrapper.setStyle("overflow", "hidden");
43730         this.wrapper.addClass('roo-grid-container');
43731
43732     }
43733     
43734     
43735     if(config.toolbar){
43736         var tool_el = this.wrapper.createChild();    
43737         this.toolbar = Roo.factory(config.toolbar);
43738         var ti = [];
43739         if (config.toolbar.items) {
43740             ti = config.toolbar.items ;
43741             delete config.toolbar.items ;
43742         }
43743         
43744         var nitems = [];
43745         this.toolbar.render(tool_el);
43746         for(var i =0;i < ti.length;i++) {
43747           //  Roo.log(['add child', items[i]]);
43748             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43749         }
43750         this.toolbar.items = nitems;
43751         
43752         delete config.toolbar;
43753     }
43754     
43755     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43756     config.grid.scrollBody = true;;
43757     config.grid.monitorWindowResize = false; // turn off autosizing
43758     config.grid.autoHeight = false;
43759     config.grid.autoWidth = false;
43760     
43761     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43762     
43763     if (config.background) {
43764         // render grid on panel activation (if panel background)
43765         this.on('activate', function(gp) {
43766             if (!gp.grid.rendered) {
43767                 gp.grid.render(this.wrapper);
43768                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43769             }
43770         });
43771             
43772     } else {
43773         this.grid.render(this.wrapper);
43774         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43775
43776     }
43777     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43778     // ??? needed ??? config.el = this.wrapper;
43779     
43780     
43781     
43782   
43783     // xtype created footer. - not sure if will work as we normally have to render first..
43784     if (this.footer && !this.footer.el && this.footer.xtype) {
43785         
43786         var ctr = this.grid.getView().getFooterPanel(true);
43787         this.footer.dataSource = this.grid.dataSource;
43788         this.footer = Roo.factory(this.footer, Roo);
43789         this.footer.render(ctr);
43790         
43791     }
43792     
43793     
43794     
43795     
43796      
43797 };
43798
43799 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43800 {
43801   
43802     getId : function(){
43803         return this.grid.id;
43804     },
43805     
43806     /**
43807      * Returns the grid for this panel
43808      * @return {Roo.bootstrap.Table} 
43809      */
43810     getGrid : function(){
43811         return this.grid;    
43812     },
43813     
43814     setSize : function(width, height)
43815     {
43816      
43817         //if(!this.ignoreResize(width, height)){
43818             var grid = this.grid;
43819             var size = this.adjustForComponents(width, height);
43820             // tfoot is not a footer?
43821           
43822             
43823             var gridel = grid.getGridEl();
43824             gridel.setSize(size.width, size.height);
43825             
43826             var tbd = grid.getGridEl().select('tbody', true).first();
43827             var thd = grid.getGridEl().select('thead',true).first();
43828             var tbf= grid.getGridEl().select('tfoot', true).first();
43829
43830             if (tbf) {
43831                 size.height -= tbf.getHeight();
43832             }
43833             if (thd) {
43834                 size.height -= thd.getHeight();
43835             }
43836             
43837             tbd.setSize(size.width, size.height );
43838             // this is for the account management tab -seems to work there.
43839             var thd = grid.getGridEl().select('thead',true).first();
43840             //if (tbd) {
43841             //    tbd.setSize(size.width, size.height - thd.getHeight());
43842             //}
43843              
43844             grid.autoSize();
43845         //}
43846    
43847     },
43848      
43849     
43850     
43851     beforeSlide : function(){
43852         this.grid.getView().scroller.clip();
43853     },
43854     
43855     afterSlide : function(){
43856         this.grid.getView().scroller.unclip();
43857     },
43858     
43859     destroy : function(){
43860         this.grid.destroy();
43861         delete this.grid;
43862         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43863     }
43864 });
43865
43866 /**
43867  * @class Roo.bootstrap.panel.Nest
43868  * @extends Roo.bootstrap.panel.Content
43869  * @constructor
43870  * Create a new Panel, that can contain a layout.Border.
43871  * 
43872  * 
43873  * @param {String/Object} config A string to set only the title or a config object
43874  */
43875 Roo.bootstrap.panel.Nest = function(config)
43876 {
43877     // construct with only one argument..
43878     /* FIXME - implement nicer consturctors
43879     if (layout.layout) {
43880         config = layout;
43881         layout = config.layout;
43882         delete config.layout;
43883     }
43884     if (layout.xtype && !layout.getEl) {
43885         // then layout needs constructing..
43886         layout = Roo.factory(layout, Roo);
43887     }
43888     */
43889     
43890     config.el =  config.layout.getEl();
43891     
43892     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43893     
43894     config.layout.monitorWindowResize = false; // turn off autosizing
43895     this.layout = config.layout;
43896     this.layout.getEl().addClass("roo-layout-nested-layout");
43897     this.layout.parent = this;
43898     
43899     
43900     
43901     
43902 };
43903
43904 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43905     /**
43906     * @cfg {Roo.BorderLayout} layout The layout for this panel
43907     */
43908     layout : false,
43909
43910     setSize : function(width, height){
43911         if(!this.ignoreResize(width, height)){
43912             var size = this.adjustForComponents(width, height);
43913             var el = this.layout.getEl();
43914             if (size.height < 1) {
43915                 el.setWidth(size.width);   
43916             } else {
43917                 el.setSize(size.width, size.height);
43918             }
43919             var touch = el.dom.offsetWidth;
43920             this.layout.layout();
43921             // ie requires a double layout on the first pass
43922             if(Roo.isIE && !this.initialized){
43923                 this.initialized = true;
43924                 this.layout.layout();
43925             }
43926         }
43927     },
43928     
43929     // activate all subpanels if not currently active..
43930     
43931     setActiveState : function(active){
43932         this.active = active;
43933         this.setActiveClass(active);
43934         
43935         if(!active){
43936             this.fireEvent("deactivate", this);
43937             return;
43938         }
43939         
43940         this.fireEvent("activate", this);
43941         // not sure if this should happen before or after..
43942         if (!this.layout) {
43943             return; // should not happen..
43944         }
43945         var reg = false;
43946         for (var r in this.layout.regions) {
43947             reg = this.layout.getRegion(r);
43948             if (reg.getActivePanel()) {
43949                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43950                 reg.setActivePanel(reg.getActivePanel());
43951                 continue;
43952             }
43953             if (!reg.panels.length) {
43954                 continue;
43955             }
43956             reg.showPanel(reg.getPanel(0));
43957         }
43958         
43959         
43960         
43961         
43962     },
43963     
43964     /**
43965      * Returns the nested BorderLayout for this panel
43966      * @return {Roo.BorderLayout} 
43967      */
43968     getLayout : function(){
43969         return this.layout;
43970     },
43971     
43972      /**
43973      * Adds a xtype elements to the layout of the nested panel
43974      * <pre><code>
43975
43976 panel.addxtype({
43977        xtype : 'ContentPanel',
43978        region: 'west',
43979        items: [ .... ]
43980    }
43981 );
43982
43983 panel.addxtype({
43984         xtype : 'NestedLayoutPanel',
43985         region: 'west',
43986         layout: {
43987            center: { },
43988            west: { }   
43989         },
43990         items : [ ... list of content panels or nested layout panels.. ]
43991    }
43992 );
43993 </code></pre>
43994      * @param {Object} cfg Xtype definition of item to add.
43995      */
43996     addxtype : function(cfg) {
43997         return this.layout.addxtype(cfg);
43998     
43999     }
44000 });/*
44001  * Based on:
44002  * Ext JS Library 1.1.1
44003  * Copyright(c) 2006-2007, Ext JS, LLC.
44004  *
44005  * Originally Released Under LGPL - original licence link has changed is not relivant.
44006  *
44007  * Fork - LGPL
44008  * <script type="text/javascript">
44009  */
44010 /**
44011  * @class Roo.TabPanel
44012  * @extends Roo.util.Observable
44013  * A lightweight tab container.
44014  * <br><br>
44015  * Usage:
44016  * <pre><code>
44017 // basic tabs 1, built from existing content
44018 var tabs = new Roo.TabPanel("tabs1");
44019 tabs.addTab("script", "View Script");
44020 tabs.addTab("markup", "View Markup");
44021 tabs.activate("script");
44022
44023 // more advanced tabs, built from javascript
44024 var jtabs = new Roo.TabPanel("jtabs");
44025 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44026
44027 // set up the UpdateManager
44028 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44029 var updater = tab2.getUpdateManager();
44030 updater.setDefaultUrl("ajax1.htm");
44031 tab2.on('activate', updater.refresh, updater, true);
44032
44033 // Use setUrl for Ajax loading
44034 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44035 tab3.setUrl("ajax2.htm", null, true);
44036
44037 // Disabled tab
44038 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44039 tab4.disable();
44040
44041 jtabs.activate("jtabs-1");
44042  * </code></pre>
44043  * @constructor
44044  * Create a new TabPanel.
44045  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44046  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44047  */
44048 Roo.bootstrap.panel.Tabs = function(config){
44049     /**
44050     * The container element for this TabPanel.
44051     * @type Roo.Element
44052     */
44053     this.el = Roo.get(config.el);
44054     delete config.el;
44055     if(config){
44056         if(typeof config == "boolean"){
44057             this.tabPosition = config ? "bottom" : "top";
44058         }else{
44059             Roo.apply(this, config);
44060         }
44061     }
44062     
44063     if(this.tabPosition == "bottom"){
44064         // if tabs are at the bottom = create the body first.
44065         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44066         this.el.addClass("roo-tabs-bottom");
44067     }
44068     // next create the tabs holders
44069     
44070     if (this.tabPosition == "west"){
44071         
44072         var reg = this.region; // fake it..
44073         while (reg) {
44074             if (!reg.mgr.parent) {
44075                 break;
44076             }
44077             reg = reg.mgr.parent.region;
44078         }
44079         Roo.log("got nest?");
44080         Roo.log(reg);
44081         if (reg.mgr.getRegion('west')) {
44082             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44083             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44084             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44085             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44086             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44087         
44088             
44089         }
44090         
44091         
44092     } else {
44093      
44094         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44095         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44096         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44097         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44098     }
44099     
44100     
44101     if(Roo.isIE){
44102         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44103     }
44104     
44105     // finally - if tabs are at the top, then create the body last..
44106     if(this.tabPosition != "bottom"){
44107         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44108          * @type Roo.Element
44109          */
44110         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44111         this.el.addClass("roo-tabs-top");
44112     }
44113     this.items = [];
44114
44115     this.bodyEl.setStyle("position", "relative");
44116
44117     this.active = null;
44118     this.activateDelegate = this.activate.createDelegate(this);
44119
44120     this.addEvents({
44121         /**
44122          * @event tabchange
44123          * Fires when the active tab changes
44124          * @param {Roo.TabPanel} this
44125          * @param {Roo.TabPanelItem} activePanel The new active tab
44126          */
44127         "tabchange": true,
44128         /**
44129          * @event beforetabchange
44130          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44131          * @param {Roo.TabPanel} this
44132          * @param {Object} e Set cancel to true on this object to cancel the tab change
44133          * @param {Roo.TabPanelItem} tab The tab being changed to
44134          */
44135         "beforetabchange" : true
44136     });
44137
44138     Roo.EventManager.onWindowResize(this.onResize, this);
44139     this.cpad = this.el.getPadding("lr");
44140     this.hiddenCount = 0;
44141
44142
44143     // toolbar on the tabbar support...
44144     if (this.toolbar) {
44145         alert("no toolbar support yet");
44146         this.toolbar  = false;
44147         /*
44148         var tcfg = this.toolbar;
44149         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44150         this.toolbar = new Roo.Toolbar(tcfg);
44151         if (Roo.isSafari) {
44152             var tbl = tcfg.container.child('table', true);
44153             tbl.setAttribute('width', '100%');
44154         }
44155         */
44156         
44157     }
44158    
44159
44160
44161     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44162 };
44163
44164 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44165     /*
44166      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44167      */
44168     tabPosition : "top",
44169     /*
44170      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44171      */
44172     currentTabWidth : 0,
44173     /*
44174      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44175      */
44176     minTabWidth : 40,
44177     /*
44178      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44179      */
44180     maxTabWidth : 250,
44181     /*
44182      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44183      */
44184     preferredTabWidth : 175,
44185     /*
44186      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44187      */
44188     resizeTabs : false,
44189     /*
44190      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44191      */
44192     monitorResize : true,
44193     /*
44194      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44195      */
44196     toolbar : false,  // set by caller..
44197     
44198     region : false, /// set by caller
44199     
44200     disableTooltips : true, // not used yet...
44201
44202     /**
44203      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44204      * @param {String} id The id of the div to use <b>or create</b>
44205      * @param {String} text The text for the tab
44206      * @param {String} content (optional) Content to put in the TabPanelItem body
44207      * @param {Boolean} closable (optional) True to create a close icon on the tab
44208      * @return {Roo.TabPanelItem} The created TabPanelItem
44209      */
44210     addTab : function(id, text, content, closable, tpl)
44211     {
44212         var item = new Roo.bootstrap.panel.TabItem({
44213             panel: this,
44214             id : id,
44215             text : text,
44216             closable : closable,
44217             tpl : tpl
44218         });
44219         this.addTabItem(item);
44220         if(content){
44221             item.setContent(content);
44222         }
44223         return item;
44224     },
44225
44226     /**
44227      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44228      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44229      * @return {Roo.TabPanelItem}
44230      */
44231     getTab : function(id){
44232         return this.items[id];
44233     },
44234
44235     /**
44236      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44237      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44238      */
44239     hideTab : function(id){
44240         var t = this.items[id];
44241         if(!t.isHidden()){
44242            t.setHidden(true);
44243            this.hiddenCount++;
44244            this.autoSizeTabs();
44245         }
44246     },
44247
44248     /**
44249      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44250      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44251      */
44252     unhideTab : function(id){
44253         var t = this.items[id];
44254         if(t.isHidden()){
44255            t.setHidden(false);
44256            this.hiddenCount--;
44257            this.autoSizeTabs();
44258         }
44259     },
44260
44261     /**
44262      * Adds an existing {@link Roo.TabPanelItem}.
44263      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44264      */
44265     addTabItem : function(item)
44266     {
44267         this.items[item.id] = item;
44268         this.items.push(item);
44269         this.autoSizeTabs();
44270       //  if(this.resizeTabs){
44271     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44272   //         this.autoSizeTabs();
44273 //        }else{
44274 //            item.autoSize();
44275        // }
44276     },
44277
44278     /**
44279      * Removes a {@link Roo.TabPanelItem}.
44280      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44281      */
44282     removeTab : function(id){
44283         var items = this.items;
44284         var tab = items[id];
44285         if(!tab) { return; }
44286         var index = items.indexOf(tab);
44287         if(this.active == tab && items.length > 1){
44288             var newTab = this.getNextAvailable(index);
44289             if(newTab) {
44290                 newTab.activate();
44291             }
44292         }
44293         this.stripEl.dom.removeChild(tab.pnode.dom);
44294         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44295             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44296         }
44297         items.splice(index, 1);
44298         delete this.items[tab.id];
44299         tab.fireEvent("close", tab);
44300         tab.purgeListeners();
44301         this.autoSizeTabs();
44302     },
44303
44304     getNextAvailable : function(start){
44305         var items = this.items;
44306         var index = start;
44307         // look for a next tab that will slide over to
44308         // replace the one being removed
44309         while(index < items.length){
44310             var item = items[++index];
44311             if(item && !item.isHidden()){
44312                 return item;
44313             }
44314         }
44315         // if one isn't found select the previous tab (on the left)
44316         index = start;
44317         while(index >= 0){
44318             var item = items[--index];
44319             if(item && !item.isHidden()){
44320                 return item;
44321             }
44322         }
44323         return null;
44324     },
44325
44326     /**
44327      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44328      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44329      */
44330     disableTab : function(id){
44331         var tab = this.items[id];
44332         if(tab && this.active != tab){
44333             tab.disable();
44334         }
44335     },
44336
44337     /**
44338      * Enables a {@link Roo.TabPanelItem} that is disabled.
44339      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44340      */
44341     enableTab : function(id){
44342         var tab = this.items[id];
44343         tab.enable();
44344     },
44345
44346     /**
44347      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44348      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44349      * @return {Roo.TabPanelItem} The TabPanelItem.
44350      */
44351     activate : function(id)
44352     {
44353         //Roo.log('activite:'  + id);
44354         
44355         var tab = this.items[id];
44356         if(!tab){
44357             return null;
44358         }
44359         if(tab == this.active || tab.disabled){
44360             return tab;
44361         }
44362         var e = {};
44363         this.fireEvent("beforetabchange", this, e, tab);
44364         if(e.cancel !== true && !tab.disabled){
44365             if(this.active){
44366                 this.active.hide();
44367             }
44368             this.active = this.items[id];
44369             this.active.show();
44370             this.fireEvent("tabchange", this, this.active);
44371         }
44372         return tab;
44373     },
44374
44375     /**
44376      * Gets the active {@link Roo.TabPanelItem}.
44377      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44378      */
44379     getActiveTab : function(){
44380         return this.active;
44381     },
44382
44383     /**
44384      * Updates the tab body element to fit the height of the container element
44385      * for overflow scrolling
44386      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44387      */
44388     syncHeight : function(targetHeight){
44389         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44390         var bm = this.bodyEl.getMargins();
44391         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44392         this.bodyEl.setHeight(newHeight);
44393         return newHeight;
44394     },
44395
44396     onResize : function(){
44397         if(this.monitorResize){
44398             this.autoSizeTabs();
44399         }
44400     },
44401
44402     /**
44403      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44404      */
44405     beginUpdate : function(){
44406         this.updating = true;
44407     },
44408
44409     /**
44410      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44411      */
44412     endUpdate : function(){
44413         this.updating = false;
44414         this.autoSizeTabs();
44415     },
44416
44417     /**
44418      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44419      */
44420     autoSizeTabs : function()
44421     {
44422         var count = this.items.length;
44423         var vcount = count - this.hiddenCount;
44424         
44425         if (vcount < 2) {
44426             this.stripEl.hide();
44427         } else {
44428             this.stripEl.show();
44429         }
44430         
44431         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44432             return;
44433         }
44434         
44435         
44436         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44437         var availWidth = Math.floor(w / vcount);
44438         var b = this.stripBody;
44439         if(b.getWidth() > w){
44440             var tabs = this.items;
44441             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44442             if(availWidth < this.minTabWidth){
44443                 /*if(!this.sleft){    // incomplete scrolling code
44444                     this.createScrollButtons();
44445                 }
44446                 this.showScroll();
44447                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44448             }
44449         }else{
44450             if(this.currentTabWidth < this.preferredTabWidth){
44451                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44452             }
44453         }
44454     },
44455
44456     /**
44457      * Returns the number of tabs in this TabPanel.
44458      * @return {Number}
44459      */
44460      getCount : function(){
44461          return this.items.length;
44462      },
44463
44464     /**
44465      * Resizes all the tabs to the passed width
44466      * @param {Number} The new width
44467      */
44468     setTabWidth : function(width){
44469         this.currentTabWidth = width;
44470         for(var i = 0, len = this.items.length; i < len; i++) {
44471                 if(!this.items[i].isHidden()) {
44472                 this.items[i].setWidth(width);
44473             }
44474         }
44475     },
44476
44477     /**
44478      * Destroys this TabPanel
44479      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44480      */
44481     destroy : function(removeEl){
44482         Roo.EventManager.removeResizeListener(this.onResize, this);
44483         for(var i = 0, len = this.items.length; i < len; i++){
44484             this.items[i].purgeListeners();
44485         }
44486         if(removeEl === true){
44487             this.el.update("");
44488             this.el.remove();
44489         }
44490     },
44491     
44492     createStrip : function(container)
44493     {
44494         var strip = document.createElement("nav");
44495         strip.className = Roo.bootstrap.version == 4 ?
44496             "navbar-light bg-light" : 
44497             "navbar navbar-default"; //"x-tabs-wrap";
44498         container.appendChild(strip);
44499         return strip;
44500     },
44501     
44502     createStripList : function(strip)
44503     {
44504         // div wrapper for retard IE
44505         // returns the "tr" element.
44506         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44507         //'<div class="x-tabs-strip-wrap">'+
44508           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44509           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44510         return strip.firstChild; //.firstChild.firstChild.firstChild;
44511     },
44512     createBody : function(container)
44513     {
44514         var body = document.createElement("div");
44515         Roo.id(body, "tab-body");
44516         //Roo.fly(body).addClass("x-tabs-body");
44517         Roo.fly(body).addClass("tab-content");
44518         container.appendChild(body);
44519         return body;
44520     },
44521     createItemBody :function(bodyEl, id){
44522         var body = Roo.getDom(id);
44523         if(!body){
44524             body = document.createElement("div");
44525             body.id = id;
44526         }
44527         //Roo.fly(body).addClass("x-tabs-item-body");
44528         Roo.fly(body).addClass("tab-pane");
44529          bodyEl.insertBefore(body, bodyEl.firstChild);
44530         return body;
44531     },
44532     /** @private */
44533     createStripElements :  function(stripEl, text, closable, tpl)
44534     {
44535         var td = document.createElement("li"); // was td..
44536         td.className = 'nav-item';
44537         
44538         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44539         
44540         
44541         stripEl.appendChild(td);
44542         /*if(closable){
44543             td.className = "x-tabs-closable";
44544             if(!this.closeTpl){
44545                 this.closeTpl = new Roo.Template(
44546                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44547                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44548                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44549                 );
44550             }
44551             var el = this.closeTpl.overwrite(td, {"text": text});
44552             var close = el.getElementsByTagName("div")[0];
44553             var inner = el.getElementsByTagName("em")[0];
44554             return {"el": el, "close": close, "inner": inner};
44555         } else {
44556         */
44557         // not sure what this is..
44558 //            if(!this.tabTpl){
44559                 //this.tabTpl = new Roo.Template(
44560                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44561                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44562                 //);
44563 //                this.tabTpl = new Roo.Template(
44564 //                   '<a href="#">' +
44565 //                   '<span unselectable="on"' +
44566 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44567 //                            ' >{text}</span></a>'
44568 //                );
44569 //                
44570 //            }
44571
44572
44573             var template = tpl || this.tabTpl || false;
44574             
44575             if(!template){
44576                 template =  new Roo.Template(
44577                         Roo.bootstrap.version == 4 ? 
44578                             (
44579                                 '<a class="nav-link" href="#" unselectable="on"' +
44580                                      (this.disableTooltips ? '' : ' title="{text}"') +
44581                                      ' >{text}</a>'
44582                             ) : (
44583                                 '<a class="nav-link" href="#">' +
44584                                 '<span unselectable="on"' +
44585                                          (this.disableTooltips ? '' : ' title="{text}"') +
44586                                     ' >{text}</span></a>'
44587                             )
44588                 );
44589             }
44590             
44591             switch (typeof(template)) {
44592                 case 'object' :
44593                     break;
44594                 case 'string' :
44595                     template = new Roo.Template(template);
44596                     break;
44597                 default :
44598                     break;
44599             }
44600             
44601             var el = template.overwrite(td, {"text": text});
44602             
44603             var inner = el.getElementsByTagName("span")[0];
44604             
44605             return {"el": el, "inner": inner};
44606             
44607     }
44608         
44609     
44610 });
44611
44612 /**
44613  * @class Roo.TabPanelItem
44614  * @extends Roo.util.Observable
44615  * Represents an individual item (tab plus body) in a TabPanel.
44616  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44617  * @param {String} id The id of this TabPanelItem
44618  * @param {String} text The text for the tab of this TabPanelItem
44619  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44620  */
44621 Roo.bootstrap.panel.TabItem = function(config){
44622     /**
44623      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44624      * @type Roo.TabPanel
44625      */
44626     this.tabPanel = config.panel;
44627     /**
44628      * The id for this TabPanelItem
44629      * @type String
44630      */
44631     this.id = config.id;
44632     /** @private */
44633     this.disabled = false;
44634     /** @private */
44635     this.text = config.text;
44636     /** @private */
44637     this.loaded = false;
44638     this.closable = config.closable;
44639
44640     /**
44641      * The body element for this TabPanelItem.
44642      * @type Roo.Element
44643      */
44644     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44645     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44646     this.bodyEl.setStyle("display", "block");
44647     this.bodyEl.setStyle("zoom", "1");
44648     //this.hideAction();
44649
44650     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44651     /** @private */
44652     this.el = Roo.get(els.el);
44653     this.inner = Roo.get(els.inner, true);
44654      this.textEl = Roo.bootstrap.version == 4 ?
44655         this.el : Roo.get(this.el.dom.firstChild, true);
44656
44657     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44658     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44659
44660     
44661 //    this.el.on("mousedown", this.onTabMouseDown, this);
44662     this.el.on("click", this.onTabClick, this);
44663     /** @private */
44664     if(config.closable){
44665         var c = Roo.get(els.close, true);
44666         c.dom.title = this.closeText;
44667         c.addClassOnOver("close-over");
44668         c.on("click", this.closeClick, this);
44669      }
44670
44671     this.addEvents({
44672          /**
44673          * @event activate
44674          * Fires when this tab becomes the active tab.
44675          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44676          * @param {Roo.TabPanelItem} this
44677          */
44678         "activate": true,
44679         /**
44680          * @event beforeclose
44681          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44682          * @param {Roo.TabPanelItem} this
44683          * @param {Object} e Set cancel to true on this object to cancel the close.
44684          */
44685         "beforeclose": true,
44686         /**
44687          * @event close
44688          * Fires when this tab is closed.
44689          * @param {Roo.TabPanelItem} this
44690          */
44691          "close": true,
44692         /**
44693          * @event deactivate
44694          * Fires when this tab is no longer the active tab.
44695          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44696          * @param {Roo.TabPanelItem} this
44697          */
44698          "deactivate" : true
44699     });
44700     this.hidden = false;
44701
44702     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44703 };
44704
44705 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44706            {
44707     purgeListeners : function(){
44708        Roo.util.Observable.prototype.purgeListeners.call(this);
44709        this.el.removeAllListeners();
44710     },
44711     /**
44712      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44713      */
44714     show : function(){
44715         this.status_node.addClass("active");
44716         this.showAction();
44717         if(Roo.isOpera){
44718             this.tabPanel.stripWrap.repaint();
44719         }
44720         this.fireEvent("activate", this.tabPanel, this);
44721     },
44722
44723     /**
44724      * Returns true if this tab is the active tab.
44725      * @return {Boolean}
44726      */
44727     isActive : function(){
44728         return this.tabPanel.getActiveTab() == this;
44729     },
44730
44731     /**
44732      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44733      */
44734     hide : function(){
44735         this.status_node.removeClass("active");
44736         this.hideAction();
44737         this.fireEvent("deactivate", this.tabPanel, this);
44738     },
44739
44740     hideAction : function(){
44741         this.bodyEl.hide();
44742         this.bodyEl.setStyle("position", "absolute");
44743         this.bodyEl.setLeft("-20000px");
44744         this.bodyEl.setTop("-20000px");
44745     },
44746
44747     showAction : function(){
44748         this.bodyEl.setStyle("position", "relative");
44749         this.bodyEl.setTop("");
44750         this.bodyEl.setLeft("");
44751         this.bodyEl.show();
44752     },
44753
44754     /**
44755      * Set the tooltip for the tab.
44756      * @param {String} tooltip The tab's tooltip
44757      */
44758     setTooltip : function(text){
44759         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44760             this.textEl.dom.qtip = text;
44761             this.textEl.dom.removeAttribute('title');
44762         }else{
44763             this.textEl.dom.title = text;
44764         }
44765     },
44766
44767     onTabClick : function(e){
44768         e.preventDefault();
44769         this.tabPanel.activate(this.id);
44770     },
44771
44772     onTabMouseDown : function(e){
44773         e.preventDefault();
44774         this.tabPanel.activate(this.id);
44775     },
44776 /*
44777     getWidth : function(){
44778         return this.inner.getWidth();
44779     },
44780
44781     setWidth : function(width){
44782         var iwidth = width - this.linode.getPadding("lr");
44783         this.inner.setWidth(iwidth);
44784         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44785         this.linode.setWidth(width);
44786     },
44787 */
44788     /**
44789      * Show or hide the tab
44790      * @param {Boolean} hidden True to hide or false to show.
44791      */
44792     setHidden : function(hidden){
44793         this.hidden = hidden;
44794         this.linode.setStyle("display", hidden ? "none" : "");
44795     },
44796
44797     /**
44798      * Returns true if this tab is "hidden"
44799      * @return {Boolean}
44800      */
44801     isHidden : function(){
44802         return this.hidden;
44803     },
44804
44805     /**
44806      * Returns the text for this tab
44807      * @return {String}
44808      */
44809     getText : function(){
44810         return this.text;
44811     },
44812     /*
44813     autoSize : function(){
44814         //this.el.beginMeasure();
44815         this.textEl.setWidth(1);
44816         /*
44817          *  #2804 [new] Tabs in Roojs
44818          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44819          */
44820         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44821         //this.el.endMeasure();
44822     //},
44823
44824     /**
44825      * Sets the text for the tab (Note: this also sets the tooltip text)
44826      * @param {String} text The tab's text and tooltip
44827      */
44828     setText : function(text){
44829         this.text = text;
44830         this.textEl.update(text);
44831         this.setTooltip(text);
44832         //if(!this.tabPanel.resizeTabs){
44833         //    this.autoSize();
44834         //}
44835     },
44836     /**
44837      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44838      */
44839     activate : function(){
44840         this.tabPanel.activate(this.id);
44841     },
44842
44843     /**
44844      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44845      */
44846     disable : function(){
44847         if(this.tabPanel.active != this){
44848             this.disabled = true;
44849             this.status_node.addClass("disabled");
44850         }
44851     },
44852
44853     /**
44854      * Enables this TabPanelItem if it was previously disabled.
44855      */
44856     enable : function(){
44857         this.disabled = false;
44858         this.status_node.removeClass("disabled");
44859     },
44860
44861     /**
44862      * Sets the content for this TabPanelItem.
44863      * @param {String} content The content
44864      * @param {Boolean} loadScripts true to look for and load scripts
44865      */
44866     setContent : function(content, loadScripts){
44867         this.bodyEl.update(content, loadScripts);
44868     },
44869
44870     /**
44871      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44872      * @return {Roo.UpdateManager} The UpdateManager
44873      */
44874     getUpdateManager : function(){
44875         return this.bodyEl.getUpdateManager();
44876     },
44877
44878     /**
44879      * Set a URL to be used to load the content for this TabPanelItem.
44880      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44881      * @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)
44882      * @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)
44883      * @return {Roo.UpdateManager} The UpdateManager
44884      */
44885     setUrl : function(url, params, loadOnce){
44886         if(this.refreshDelegate){
44887             this.un('activate', this.refreshDelegate);
44888         }
44889         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44890         this.on("activate", this.refreshDelegate);
44891         return this.bodyEl.getUpdateManager();
44892     },
44893
44894     /** @private */
44895     _handleRefresh : function(url, params, loadOnce){
44896         if(!loadOnce || !this.loaded){
44897             var updater = this.bodyEl.getUpdateManager();
44898             updater.update(url, params, this._setLoaded.createDelegate(this));
44899         }
44900     },
44901
44902     /**
44903      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44904      *   Will fail silently if the setUrl method has not been called.
44905      *   This does not activate the panel, just updates its content.
44906      */
44907     refresh : function(){
44908         if(this.refreshDelegate){
44909            this.loaded = false;
44910            this.refreshDelegate();
44911         }
44912     },
44913
44914     /** @private */
44915     _setLoaded : function(){
44916         this.loaded = true;
44917     },
44918
44919     /** @private */
44920     closeClick : function(e){
44921         var o = {};
44922         e.stopEvent();
44923         this.fireEvent("beforeclose", this, o);
44924         if(o.cancel !== true){
44925             this.tabPanel.removeTab(this.id);
44926         }
44927     },
44928     /**
44929      * The text displayed in the tooltip for the close icon.
44930      * @type String
44931      */
44932     closeText : "Close this tab"
44933 });
44934 /**
44935 *    This script refer to:
44936 *    Title: International Telephone Input
44937 *    Author: Jack O'Connor
44938 *    Code version:  v12.1.12
44939 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44940 **/
44941
44942 Roo.bootstrap.form.PhoneInputData = function() {
44943     var d = [
44944       [
44945         "Afghanistan (‫افغانستان‬‎)",
44946         "af",
44947         "93"
44948       ],
44949       [
44950         "Albania (Shqipëri)",
44951         "al",
44952         "355"
44953       ],
44954       [
44955         "Algeria (‫الجزائر‬‎)",
44956         "dz",
44957         "213"
44958       ],
44959       [
44960         "American Samoa",
44961         "as",
44962         "1684"
44963       ],
44964       [
44965         "Andorra",
44966         "ad",
44967         "376"
44968       ],
44969       [
44970         "Angola",
44971         "ao",
44972         "244"
44973       ],
44974       [
44975         "Anguilla",
44976         "ai",
44977         "1264"
44978       ],
44979       [
44980         "Antigua and Barbuda",
44981         "ag",
44982         "1268"
44983       ],
44984       [
44985         "Argentina",
44986         "ar",
44987         "54"
44988       ],
44989       [
44990         "Armenia (Հայաստան)",
44991         "am",
44992         "374"
44993       ],
44994       [
44995         "Aruba",
44996         "aw",
44997         "297"
44998       ],
44999       [
45000         "Australia",
45001         "au",
45002         "61",
45003         0
45004       ],
45005       [
45006         "Austria (Österreich)",
45007         "at",
45008         "43"
45009       ],
45010       [
45011         "Azerbaijan (Azərbaycan)",
45012         "az",
45013         "994"
45014       ],
45015       [
45016         "Bahamas",
45017         "bs",
45018         "1242"
45019       ],
45020       [
45021         "Bahrain (‫البحرين‬‎)",
45022         "bh",
45023         "973"
45024       ],
45025       [
45026         "Bangladesh (বাংলাদেশ)",
45027         "bd",
45028         "880"
45029       ],
45030       [
45031         "Barbados",
45032         "bb",
45033         "1246"
45034       ],
45035       [
45036         "Belarus (Беларусь)",
45037         "by",
45038         "375"
45039       ],
45040       [
45041         "Belgium (België)",
45042         "be",
45043         "32"
45044       ],
45045       [
45046         "Belize",
45047         "bz",
45048         "501"
45049       ],
45050       [
45051         "Benin (Bénin)",
45052         "bj",
45053         "229"
45054       ],
45055       [
45056         "Bermuda",
45057         "bm",
45058         "1441"
45059       ],
45060       [
45061         "Bhutan (འབྲུག)",
45062         "bt",
45063         "975"
45064       ],
45065       [
45066         "Bolivia",
45067         "bo",
45068         "591"
45069       ],
45070       [
45071         "Bosnia and Herzegovina (Босна и Херцеговина)",
45072         "ba",
45073         "387"
45074       ],
45075       [
45076         "Botswana",
45077         "bw",
45078         "267"
45079       ],
45080       [
45081         "Brazil (Brasil)",
45082         "br",
45083         "55"
45084       ],
45085       [
45086         "British Indian Ocean Territory",
45087         "io",
45088         "246"
45089       ],
45090       [
45091         "British Virgin Islands",
45092         "vg",
45093         "1284"
45094       ],
45095       [
45096         "Brunei",
45097         "bn",
45098         "673"
45099       ],
45100       [
45101         "Bulgaria (България)",
45102         "bg",
45103         "359"
45104       ],
45105       [
45106         "Burkina Faso",
45107         "bf",
45108         "226"
45109       ],
45110       [
45111         "Burundi (Uburundi)",
45112         "bi",
45113         "257"
45114       ],
45115       [
45116         "Cambodia (កម្ពុជា)",
45117         "kh",
45118         "855"
45119       ],
45120       [
45121         "Cameroon (Cameroun)",
45122         "cm",
45123         "237"
45124       ],
45125       [
45126         "Canada",
45127         "ca",
45128         "1",
45129         1,
45130         ["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"]
45131       ],
45132       [
45133         "Cape Verde (Kabu Verdi)",
45134         "cv",
45135         "238"
45136       ],
45137       [
45138         "Caribbean Netherlands",
45139         "bq",
45140         "599",
45141         1
45142       ],
45143       [
45144         "Cayman Islands",
45145         "ky",
45146         "1345"
45147       ],
45148       [
45149         "Central African Republic (République centrafricaine)",
45150         "cf",
45151         "236"
45152       ],
45153       [
45154         "Chad (Tchad)",
45155         "td",
45156         "235"
45157       ],
45158       [
45159         "Chile",
45160         "cl",
45161         "56"
45162       ],
45163       [
45164         "China (中国)",
45165         "cn",
45166         "86"
45167       ],
45168       [
45169         "Christmas Island",
45170         "cx",
45171         "61",
45172         2
45173       ],
45174       [
45175         "Cocos (Keeling) Islands",
45176         "cc",
45177         "61",
45178         1
45179       ],
45180       [
45181         "Colombia",
45182         "co",
45183         "57"
45184       ],
45185       [
45186         "Comoros (‫جزر القمر‬‎)",
45187         "km",
45188         "269"
45189       ],
45190       [
45191         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45192         "cd",
45193         "243"
45194       ],
45195       [
45196         "Congo (Republic) (Congo-Brazzaville)",
45197         "cg",
45198         "242"
45199       ],
45200       [
45201         "Cook Islands",
45202         "ck",
45203         "682"
45204       ],
45205       [
45206         "Costa Rica",
45207         "cr",
45208         "506"
45209       ],
45210       [
45211         "Côte d’Ivoire",
45212         "ci",
45213         "225"
45214       ],
45215       [
45216         "Croatia (Hrvatska)",
45217         "hr",
45218         "385"
45219       ],
45220       [
45221         "Cuba",
45222         "cu",
45223         "53"
45224       ],
45225       [
45226         "Curaçao",
45227         "cw",
45228         "599",
45229         0
45230       ],
45231       [
45232         "Cyprus (Κύπρος)",
45233         "cy",
45234         "357"
45235       ],
45236       [
45237         "Czech Republic (Česká republika)",
45238         "cz",
45239         "420"
45240       ],
45241       [
45242         "Denmark (Danmark)",
45243         "dk",
45244         "45"
45245       ],
45246       [
45247         "Djibouti",
45248         "dj",
45249         "253"
45250       ],
45251       [
45252         "Dominica",
45253         "dm",
45254         "1767"
45255       ],
45256       [
45257         "Dominican Republic (República Dominicana)",
45258         "do",
45259         "1",
45260         2,
45261         ["809", "829", "849"]
45262       ],
45263       [
45264         "Ecuador",
45265         "ec",
45266         "593"
45267       ],
45268       [
45269         "Egypt (‫مصر‬‎)",
45270         "eg",
45271         "20"
45272       ],
45273       [
45274         "El Salvador",
45275         "sv",
45276         "503"
45277       ],
45278       [
45279         "Equatorial Guinea (Guinea Ecuatorial)",
45280         "gq",
45281         "240"
45282       ],
45283       [
45284         "Eritrea",
45285         "er",
45286         "291"
45287       ],
45288       [
45289         "Estonia (Eesti)",
45290         "ee",
45291         "372"
45292       ],
45293       [
45294         "Ethiopia",
45295         "et",
45296         "251"
45297       ],
45298       [
45299         "Falkland Islands (Islas Malvinas)",
45300         "fk",
45301         "500"
45302       ],
45303       [
45304         "Faroe Islands (Føroyar)",
45305         "fo",
45306         "298"
45307       ],
45308       [
45309         "Fiji",
45310         "fj",
45311         "679"
45312       ],
45313       [
45314         "Finland (Suomi)",
45315         "fi",
45316         "358",
45317         0
45318       ],
45319       [
45320         "France",
45321         "fr",
45322         "33"
45323       ],
45324       [
45325         "French Guiana (Guyane française)",
45326         "gf",
45327         "594"
45328       ],
45329       [
45330         "French Polynesia (Polynésie française)",
45331         "pf",
45332         "689"
45333       ],
45334       [
45335         "Gabon",
45336         "ga",
45337         "241"
45338       ],
45339       [
45340         "Gambia",
45341         "gm",
45342         "220"
45343       ],
45344       [
45345         "Georgia (საქართველო)",
45346         "ge",
45347         "995"
45348       ],
45349       [
45350         "Germany (Deutschland)",
45351         "de",
45352         "49"
45353       ],
45354       [
45355         "Ghana (Gaana)",
45356         "gh",
45357         "233"
45358       ],
45359       [
45360         "Gibraltar",
45361         "gi",
45362         "350"
45363       ],
45364       [
45365         "Greece (Ελλάδα)",
45366         "gr",
45367         "30"
45368       ],
45369       [
45370         "Greenland (Kalaallit Nunaat)",
45371         "gl",
45372         "299"
45373       ],
45374       [
45375         "Grenada",
45376         "gd",
45377         "1473"
45378       ],
45379       [
45380         "Guadeloupe",
45381         "gp",
45382         "590",
45383         0
45384       ],
45385       [
45386         "Guam",
45387         "gu",
45388         "1671"
45389       ],
45390       [
45391         "Guatemala",
45392         "gt",
45393         "502"
45394       ],
45395       [
45396         "Guernsey",
45397         "gg",
45398         "44",
45399         1
45400       ],
45401       [
45402         "Guinea (Guinée)",
45403         "gn",
45404         "224"
45405       ],
45406       [
45407         "Guinea-Bissau (Guiné Bissau)",
45408         "gw",
45409         "245"
45410       ],
45411       [
45412         "Guyana",
45413         "gy",
45414         "592"
45415       ],
45416       [
45417         "Haiti",
45418         "ht",
45419         "509"
45420       ],
45421       [
45422         "Honduras",
45423         "hn",
45424         "504"
45425       ],
45426       [
45427         "Hong Kong (香港)",
45428         "hk",
45429         "852"
45430       ],
45431       [
45432         "Hungary (Magyarország)",
45433         "hu",
45434         "36"
45435       ],
45436       [
45437         "Iceland (Ísland)",
45438         "is",
45439         "354"
45440       ],
45441       [
45442         "India (भारत)",
45443         "in",
45444         "91"
45445       ],
45446       [
45447         "Indonesia",
45448         "id",
45449         "62"
45450       ],
45451       [
45452         "Iran (‫ایران‬‎)",
45453         "ir",
45454         "98"
45455       ],
45456       [
45457         "Iraq (‫العراق‬‎)",
45458         "iq",
45459         "964"
45460       ],
45461       [
45462         "Ireland",
45463         "ie",
45464         "353"
45465       ],
45466       [
45467         "Isle of Man",
45468         "im",
45469         "44",
45470         2
45471       ],
45472       [
45473         "Israel (‫ישראל‬‎)",
45474         "il",
45475         "972"
45476       ],
45477       [
45478         "Italy (Italia)",
45479         "it",
45480         "39",
45481         0
45482       ],
45483       [
45484         "Jamaica",
45485         "jm",
45486         "1876"
45487       ],
45488       [
45489         "Japan (日本)",
45490         "jp",
45491         "81"
45492       ],
45493       [
45494         "Jersey",
45495         "je",
45496         "44",
45497         3
45498       ],
45499       [
45500         "Jordan (‫الأردن‬‎)",
45501         "jo",
45502         "962"
45503       ],
45504       [
45505         "Kazakhstan (Казахстан)",
45506         "kz",
45507         "7",
45508         1
45509       ],
45510       [
45511         "Kenya",
45512         "ke",
45513         "254"
45514       ],
45515       [
45516         "Kiribati",
45517         "ki",
45518         "686"
45519       ],
45520       [
45521         "Kosovo",
45522         "xk",
45523         "383"
45524       ],
45525       [
45526         "Kuwait (‫الكويت‬‎)",
45527         "kw",
45528         "965"
45529       ],
45530       [
45531         "Kyrgyzstan (Кыргызстан)",
45532         "kg",
45533         "996"
45534       ],
45535       [
45536         "Laos (ລາວ)",
45537         "la",
45538         "856"
45539       ],
45540       [
45541         "Latvia (Latvija)",
45542         "lv",
45543         "371"
45544       ],
45545       [
45546         "Lebanon (‫لبنان‬‎)",
45547         "lb",
45548         "961"
45549       ],
45550       [
45551         "Lesotho",
45552         "ls",
45553         "266"
45554       ],
45555       [
45556         "Liberia",
45557         "lr",
45558         "231"
45559       ],
45560       [
45561         "Libya (‫ليبيا‬‎)",
45562         "ly",
45563         "218"
45564       ],
45565       [
45566         "Liechtenstein",
45567         "li",
45568         "423"
45569       ],
45570       [
45571         "Lithuania (Lietuva)",
45572         "lt",
45573         "370"
45574       ],
45575       [
45576         "Luxembourg",
45577         "lu",
45578         "352"
45579       ],
45580       [
45581         "Macau (澳門)",
45582         "mo",
45583         "853"
45584       ],
45585       [
45586         "Macedonia (FYROM) (Македонија)",
45587         "mk",
45588         "389"
45589       ],
45590       [
45591         "Madagascar (Madagasikara)",
45592         "mg",
45593         "261"
45594       ],
45595       [
45596         "Malawi",
45597         "mw",
45598         "265"
45599       ],
45600       [
45601         "Malaysia",
45602         "my",
45603         "60"
45604       ],
45605       [
45606         "Maldives",
45607         "mv",
45608         "960"
45609       ],
45610       [
45611         "Mali",
45612         "ml",
45613         "223"
45614       ],
45615       [
45616         "Malta",
45617         "mt",
45618         "356"
45619       ],
45620       [
45621         "Marshall Islands",
45622         "mh",
45623         "692"
45624       ],
45625       [
45626         "Martinique",
45627         "mq",
45628         "596"
45629       ],
45630       [
45631         "Mauritania (‫موريتانيا‬‎)",
45632         "mr",
45633         "222"
45634       ],
45635       [
45636         "Mauritius (Moris)",
45637         "mu",
45638         "230"
45639       ],
45640       [
45641         "Mayotte",
45642         "yt",
45643         "262",
45644         1
45645       ],
45646       [
45647         "Mexico (México)",
45648         "mx",
45649         "52"
45650       ],
45651       [
45652         "Micronesia",
45653         "fm",
45654         "691"
45655       ],
45656       [
45657         "Moldova (Republica Moldova)",
45658         "md",
45659         "373"
45660       ],
45661       [
45662         "Monaco",
45663         "mc",
45664         "377"
45665       ],
45666       [
45667         "Mongolia (Монгол)",
45668         "mn",
45669         "976"
45670       ],
45671       [
45672         "Montenegro (Crna Gora)",
45673         "me",
45674         "382"
45675       ],
45676       [
45677         "Montserrat",
45678         "ms",
45679         "1664"
45680       ],
45681       [
45682         "Morocco (‫المغرب‬‎)",
45683         "ma",
45684         "212",
45685         0
45686       ],
45687       [
45688         "Mozambique (Moçambique)",
45689         "mz",
45690         "258"
45691       ],
45692       [
45693         "Myanmar (Burma) (မြန်မာ)",
45694         "mm",
45695         "95"
45696       ],
45697       [
45698         "Namibia (Namibië)",
45699         "na",
45700         "264"
45701       ],
45702       [
45703         "Nauru",
45704         "nr",
45705         "674"
45706       ],
45707       [
45708         "Nepal (नेपाल)",
45709         "np",
45710         "977"
45711       ],
45712       [
45713         "Netherlands (Nederland)",
45714         "nl",
45715         "31"
45716       ],
45717       [
45718         "New Caledonia (Nouvelle-Calédonie)",
45719         "nc",
45720         "687"
45721       ],
45722       [
45723         "New Zealand",
45724         "nz",
45725         "64"
45726       ],
45727       [
45728         "Nicaragua",
45729         "ni",
45730         "505"
45731       ],
45732       [
45733         "Niger (Nijar)",
45734         "ne",
45735         "227"
45736       ],
45737       [
45738         "Nigeria",
45739         "ng",
45740         "234"
45741       ],
45742       [
45743         "Niue",
45744         "nu",
45745         "683"
45746       ],
45747       [
45748         "Norfolk Island",
45749         "nf",
45750         "672"
45751       ],
45752       [
45753         "North Korea (조선 민주주의 인민 공화국)",
45754         "kp",
45755         "850"
45756       ],
45757       [
45758         "Northern Mariana Islands",
45759         "mp",
45760         "1670"
45761       ],
45762       [
45763         "Norway (Norge)",
45764         "no",
45765         "47",
45766         0
45767       ],
45768       [
45769         "Oman (‫عُمان‬‎)",
45770         "om",
45771         "968"
45772       ],
45773       [
45774         "Pakistan (‫پاکستان‬‎)",
45775         "pk",
45776         "92"
45777       ],
45778       [
45779         "Palau",
45780         "pw",
45781         "680"
45782       ],
45783       [
45784         "Palestine (‫فلسطين‬‎)",
45785         "ps",
45786         "970"
45787       ],
45788       [
45789         "Panama (Panamá)",
45790         "pa",
45791         "507"
45792       ],
45793       [
45794         "Papua New Guinea",
45795         "pg",
45796         "675"
45797       ],
45798       [
45799         "Paraguay",
45800         "py",
45801         "595"
45802       ],
45803       [
45804         "Peru (Perú)",
45805         "pe",
45806         "51"
45807       ],
45808       [
45809         "Philippines",
45810         "ph",
45811         "63"
45812       ],
45813       [
45814         "Poland (Polska)",
45815         "pl",
45816         "48"
45817       ],
45818       [
45819         "Portugal",
45820         "pt",
45821         "351"
45822       ],
45823       [
45824         "Puerto Rico",
45825         "pr",
45826         "1",
45827         3,
45828         ["787", "939"]
45829       ],
45830       [
45831         "Qatar (‫قطر‬‎)",
45832         "qa",
45833         "974"
45834       ],
45835       [
45836         "Réunion (La Réunion)",
45837         "re",
45838         "262",
45839         0
45840       ],
45841       [
45842         "Romania (România)",
45843         "ro",
45844         "40"
45845       ],
45846       [
45847         "Russia (Россия)",
45848         "ru",
45849         "7",
45850         0
45851       ],
45852       [
45853         "Rwanda",
45854         "rw",
45855         "250"
45856       ],
45857       [
45858         "Saint Barthélemy",
45859         "bl",
45860         "590",
45861         1
45862       ],
45863       [
45864         "Saint Helena",
45865         "sh",
45866         "290"
45867       ],
45868       [
45869         "Saint Kitts and Nevis",
45870         "kn",
45871         "1869"
45872       ],
45873       [
45874         "Saint Lucia",
45875         "lc",
45876         "1758"
45877       ],
45878       [
45879         "Saint Martin (Saint-Martin (partie française))",
45880         "mf",
45881         "590",
45882         2
45883       ],
45884       [
45885         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45886         "pm",
45887         "508"
45888       ],
45889       [
45890         "Saint Vincent and the Grenadines",
45891         "vc",
45892         "1784"
45893       ],
45894       [
45895         "Samoa",
45896         "ws",
45897         "685"
45898       ],
45899       [
45900         "San Marino",
45901         "sm",
45902         "378"
45903       ],
45904       [
45905         "São Tomé and Príncipe (São Tomé e Príncipe)",
45906         "st",
45907         "239"
45908       ],
45909       [
45910         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45911         "sa",
45912         "966"
45913       ],
45914       [
45915         "Senegal (Sénégal)",
45916         "sn",
45917         "221"
45918       ],
45919       [
45920         "Serbia (Србија)",
45921         "rs",
45922         "381"
45923       ],
45924       [
45925         "Seychelles",
45926         "sc",
45927         "248"
45928       ],
45929       [
45930         "Sierra Leone",
45931         "sl",
45932         "232"
45933       ],
45934       [
45935         "Singapore",
45936         "sg",
45937         "65"
45938       ],
45939       [
45940         "Sint Maarten",
45941         "sx",
45942         "1721"
45943       ],
45944       [
45945         "Slovakia (Slovensko)",
45946         "sk",
45947         "421"
45948       ],
45949       [
45950         "Slovenia (Slovenija)",
45951         "si",
45952         "386"
45953       ],
45954       [
45955         "Solomon Islands",
45956         "sb",
45957         "677"
45958       ],
45959       [
45960         "Somalia (Soomaaliya)",
45961         "so",
45962         "252"
45963       ],
45964       [
45965         "South Africa",
45966         "za",
45967         "27"
45968       ],
45969       [
45970         "South Korea (대한민국)",
45971         "kr",
45972         "82"
45973       ],
45974       [
45975         "South Sudan (‫جنوب السودان‬‎)",
45976         "ss",
45977         "211"
45978       ],
45979       [
45980         "Spain (España)",
45981         "es",
45982         "34"
45983       ],
45984       [
45985         "Sri Lanka (ශ්‍රී ලංකාව)",
45986         "lk",
45987         "94"
45988       ],
45989       [
45990         "Sudan (‫السودان‬‎)",
45991         "sd",
45992         "249"
45993       ],
45994       [
45995         "Suriname",
45996         "sr",
45997         "597"
45998       ],
45999       [
46000         "Svalbard and Jan Mayen",
46001         "sj",
46002         "47",
46003         1
46004       ],
46005       [
46006         "Swaziland",
46007         "sz",
46008         "268"
46009       ],
46010       [
46011         "Sweden (Sverige)",
46012         "se",
46013         "46"
46014       ],
46015       [
46016         "Switzerland (Schweiz)",
46017         "ch",
46018         "41"
46019       ],
46020       [
46021         "Syria (‫سوريا‬‎)",
46022         "sy",
46023         "963"
46024       ],
46025       [
46026         "Taiwan (台灣)",
46027         "tw",
46028         "886"
46029       ],
46030       [
46031         "Tajikistan",
46032         "tj",
46033         "992"
46034       ],
46035       [
46036         "Tanzania",
46037         "tz",
46038         "255"
46039       ],
46040       [
46041         "Thailand (ไทย)",
46042         "th",
46043         "66"
46044       ],
46045       [
46046         "Timor-Leste",
46047         "tl",
46048         "670"
46049       ],
46050       [
46051         "Togo",
46052         "tg",
46053         "228"
46054       ],
46055       [
46056         "Tokelau",
46057         "tk",
46058         "690"
46059       ],
46060       [
46061         "Tonga",
46062         "to",
46063         "676"
46064       ],
46065       [
46066         "Trinidad and Tobago",
46067         "tt",
46068         "1868"
46069       ],
46070       [
46071         "Tunisia (‫تونس‬‎)",
46072         "tn",
46073         "216"
46074       ],
46075       [
46076         "Turkey (Türkiye)",
46077         "tr",
46078         "90"
46079       ],
46080       [
46081         "Turkmenistan",
46082         "tm",
46083         "993"
46084       ],
46085       [
46086         "Turks and Caicos Islands",
46087         "tc",
46088         "1649"
46089       ],
46090       [
46091         "Tuvalu",
46092         "tv",
46093         "688"
46094       ],
46095       [
46096         "U.S. Virgin Islands",
46097         "vi",
46098         "1340"
46099       ],
46100       [
46101         "Uganda",
46102         "ug",
46103         "256"
46104       ],
46105       [
46106         "Ukraine (Україна)",
46107         "ua",
46108         "380"
46109       ],
46110       [
46111         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46112         "ae",
46113         "971"
46114       ],
46115       [
46116         "United Kingdom",
46117         "gb",
46118         "44",
46119         0
46120       ],
46121       [
46122         "United States",
46123         "us",
46124         "1",
46125         0
46126       ],
46127       [
46128         "Uruguay",
46129         "uy",
46130         "598"
46131       ],
46132       [
46133         "Uzbekistan (Oʻzbekiston)",
46134         "uz",
46135         "998"
46136       ],
46137       [
46138         "Vanuatu",
46139         "vu",
46140         "678"
46141       ],
46142       [
46143         "Vatican City (Città del Vaticano)",
46144         "va",
46145         "39",
46146         1
46147       ],
46148       [
46149         "Venezuela",
46150         "ve",
46151         "58"
46152       ],
46153       [
46154         "Vietnam (Việt Nam)",
46155         "vn",
46156         "84"
46157       ],
46158       [
46159         "Wallis and Futuna (Wallis-et-Futuna)",
46160         "wf",
46161         "681"
46162       ],
46163       [
46164         "Western Sahara (‫الصحراء الغربية‬‎)",
46165         "eh",
46166         "212",
46167         1
46168       ],
46169       [
46170         "Yemen (‫اليمن‬‎)",
46171         "ye",
46172         "967"
46173       ],
46174       [
46175         "Zambia",
46176         "zm",
46177         "260"
46178       ],
46179       [
46180         "Zimbabwe",
46181         "zw",
46182         "263"
46183       ],
46184       [
46185         "Åland Islands",
46186         "ax",
46187         "358",
46188         1
46189       ]
46190   ];
46191   
46192   return d;
46193 }/**
46194 *    This script refer to:
46195 *    Title: International Telephone Input
46196 *    Author: Jack O'Connor
46197 *    Code version:  v12.1.12
46198 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46199 **/
46200
46201 /**
46202  * @class Roo.bootstrap.form.PhoneInput
46203  * @extends Roo.bootstrap.form.TriggerField
46204  * An input with International dial-code selection
46205  
46206  * @cfg {String} defaultDialCode default '+852'
46207  * @cfg {Array} preferedCountries default []
46208   
46209  * @constructor
46210  * Create a new PhoneInput.
46211  * @param {Object} config Configuration options
46212  */
46213
46214 Roo.bootstrap.form.PhoneInput = function(config) {
46215     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46216 };
46217
46218 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46219         /**
46220         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46221         */
46222         listWidth: undefined,
46223         
46224         selectedClass: 'active',
46225         
46226         invalidClass : "has-warning",
46227         
46228         validClass: 'has-success',
46229         
46230         allowed: '0123456789',
46231         
46232         max_length: 15,
46233         
46234         /**
46235          * @cfg {String} defaultDialCode The default dial code when initializing the input
46236          */
46237         defaultDialCode: '+852',
46238         
46239         /**
46240          * @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
46241          */
46242         preferedCountries: false,
46243         
46244         getAutoCreate : function()
46245         {
46246             var data = Roo.bootstrap.form.PhoneInputData();
46247             var align = this.labelAlign || this.parentLabelAlign();
46248             var id = Roo.id();
46249             
46250             this.allCountries = [];
46251             this.dialCodeMapping = [];
46252             
46253             for (var i = 0; i < data.length; i++) {
46254               var c = data[i];
46255               this.allCountries[i] = {
46256                 name: c[0],
46257                 iso2: c[1],
46258                 dialCode: c[2],
46259                 priority: c[3] || 0,
46260                 areaCodes: c[4] || null
46261               };
46262               this.dialCodeMapping[c[2]] = {
46263                   name: c[0],
46264                   iso2: c[1],
46265                   priority: c[3] || 0,
46266                   areaCodes: c[4] || null
46267               };
46268             }
46269             
46270             var cfg = {
46271                 cls: 'form-group',
46272                 cn: []
46273             };
46274             
46275             var input =  {
46276                 tag: 'input',
46277                 id : id,
46278                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46279                 maxlength: this.max_length,
46280                 cls : 'form-control tel-input',
46281                 autocomplete: 'new-password'
46282             };
46283             
46284             var hiddenInput = {
46285                 tag: 'input',
46286                 type: 'hidden',
46287                 cls: 'hidden-tel-input'
46288             };
46289             
46290             if (this.name) {
46291                 hiddenInput.name = this.name;
46292             }
46293             
46294             if (this.disabled) {
46295                 input.disabled = true;
46296             }
46297             
46298             var flag_container = {
46299                 tag: 'div',
46300                 cls: 'flag-box',
46301                 cn: [
46302                     {
46303                         tag: 'div',
46304                         cls: 'flag'
46305                     },
46306                     {
46307                         tag: 'div',
46308                         cls: 'caret'
46309                     }
46310                 ]
46311             };
46312             
46313             var box = {
46314                 tag: 'div',
46315                 cls: this.hasFeedback ? 'has-feedback' : '',
46316                 cn: [
46317                     hiddenInput,
46318                     input,
46319                     {
46320                         tag: 'input',
46321                         cls: 'dial-code-holder',
46322                         disabled: true
46323                     }
46324                 ]
46325             };
46326             
46327             var container = {
46328                 cls: 'roo-select2-container input-group',
46329                 cn: [
46330                     flag_container,
46331                     box
46332                 ]
46333             };
46334             
46335             if (this.fieldLabel.length) {
46336                 var indicator = {
46337                     tag: 'i',
46338                     tooltip: 'This field is required'
46339                 };
46340                 
46341                 var label = {
46342                     tag: 'label',
46343                     'for':  id,
46344                     cls: 'control-label',
46345                     cn: []
46346                 };
46347                 
46348                 var label_text = {
46349                     tag: 'span',
46350                     html: this.fieldLabel
46351                 };
46352                 
46353                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46354                 label.cn = [
46355                     indicator,
46356                     label_text
46357                 ];
46358                 
46359                 if(this.indicatorpos == 'right') {
46360                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46361                     label.cn = [
46362                         label_text,
46363                         indicator
46364                     ];
46365                 }
46366                 
46367                 if(align == 'left') {
46368                     container = {
46369                         tag: 'div',
46370                         cn: [
46371                             container
46372                         ]
46373                     };
46374                     
46375                     if(this.labelWidth > 12){
46376                         label.style = "width: " + this.labelWidth + 'px';
46377                     }
46378                     if(this.labelWidth < 13 && this.labelmd == 0){
46379                         this.labelmd = this.labelWidth;
46380                     }
46381                     if(this.labellg > 0){
46382                         label.cls += ' col-lg-' + this.labellg;
46383                         input.cls += ' col-lg-' + (12 - this.labellg);
46384                     }
46385                     if(this.labelmd > 0){
46386                         label.cls += ' col-md-' + this.labelmd;
46387                         container.cls += ' col-md-' + (12 - this.labelmd);
46388                     }
46389                     if(this.labelsm > 0){
46390                         label.cls += ' col-sm-' + this.labelsm;
46391                         container.cls += ' col-sm-' + (12 - this.labelsm);
46392                     }
46393                     if(this.labelxs > 0){
46394                         label.cls += ' col-xs-' + this.labelxs;
46395                         container.cls += ' col-xs-' + (12 - this.labelxs);
46396                     }
46397                 }
46398             }
46399             
46400             cfg.cn = [
46401                 label,
46402                 container
46403             ];
46404             
46405             var settings = this;
46406             
46407             ['xs','sm','md','lg'].map(function(size){
46408                 if (settings[size]) {
46409                     cfg.cls += ' col-' + size + '-' + settings[size];
46410                 }
46411             });
46412             
46413             this.store = new Roo.data.Store({
46414                 proxy : new Roo.data.MemoryProxy({}),
46415                 reader : new Roo.data.JsonReader({
46416                     fields : [
46417                         {
46418                             'name' : 'name',
46419                             'type' : 'string'
46420                         },
46421                         {
46422                             'name' : 'iso2',
46423                             'type' : 'string'
46424                         },
46425                         {
46426                             'name' : 'dialCode',
46427                             'type' : 'string'
46428                         },
46429                         {
46430                             'name' : 'priority',
46431                             'type' : 'string'
46432                         },
46433                         {
46434                             'name' : 'areaCodes',
46435                             'type' : 'string'
46436                         }
46437                     ]
46438                 })
46439             });
46440             
46441             if(!this.preferedCountries) {
46442                 this.preferedCountries = [
46443                     'hk',
46444                     'gb',
46445                     'us'
46446                 ];
46447             }
46448             
46449             var p = this.preferedCountries.reverse();
46450             
46451             if(p) {
46452                 for (var i = 0; i < p.length; i++) {
46453                     for (var j = 0; j < this.allCountries.length; j++) {
46454                         if(this.allCountries[j].iso2 == p[i]) {
46455                             var t = this.allCountries[j];
46456                             this.allCountries.splice(j,1);
46457                             this.allCountries.unshift(t);
46458                         }
46459                     } 
46460                 }
46461             }
46462             
46463             this.store.proxy.data = {
46464                 success: true,
46465                 data: this.allCountries
46466             };
46467             
46468             return cfg;
46469         },
46470         
46471         initEvents : function()
46472         {
46473             this.createList();
46474             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46475             
46476             this.indicator = this.indicatorEl();
46477             this.flag = this.flagEl();
46478             this.dialCodeHolder = this.dialCodeHolderEl();
46479             
46480             this.trigger = this.el.select('div.flag-box',true).first();
46481             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46482             
46483             var _this = this;
46484             
46485             (function(){
46486                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46487                 _this.list.setWidth(lw);
46488             }).defer(100);
46489             
46490             this.list.on('mouseover', this.onViewOver, this);
46491             this.list.on('mousemove', this.onViewMove, this);
46492             this.inputEl().on("keyup", this.onKeyUp, this);
46493             this.inputEl().on("keypress", this.onKeyPress, this);
46494             
46495             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46496
46497             this.view = new Roo.View(this.list, this.tpl, {
46498                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46499             });
46500             
46501             this.view.on('click', this.onViewClick, this);
46502             this.setValue(this.defaultDialCode);
46503         },
46504         
46505         onTriggerClick : function(e)
46506         {
46507             Roo.log('trigger click');
46508             if(this.disabled){
46509                 return;
46510             }
46511             
46512             if(this.isExpanded()){
46513                 this.collapse();
46514                 this.hasFocus = false;
46515             }else {
46516                 this.store.load({});
46517                 this.hasFocus = true;
46518                 this.expand();
46519             }
46520         },
46521         
46522         isExpanded : function()
46523         {
46524             return this.list.isVisible();
46525         },
46526         
46527         collapse : function()
46528         {
46529             if(!this.isExpanded()){
46530                 return;
46531             }
46532             this.list.hide();
46533             Roo.get(document).un('mousedown', this.collapseIf, this);
46534             Roo.get(document).un('mousewheel', this.collapseIf, this);
46535             this.fireEvent('collapse', this);
46536             this.validate();
46537         },
46538         
46539         expand : function()
46540         {
46541             Roo.log('expand');
46542
46543             if(this.isExpanded() || !this.hasFocus){
46544                 return;
46545             }
46546             
46547             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46548             this.list.setWidth(lw);
46549             
46550             this.list.show();
46551             this.restrictHeight();
46552             
46553             Roo.get(document).on('mousedown', this.collapseIf, this);
46554             Roo.get(document).on('mousewheel', this.collapseIf, this);
46555             
46556             this.fireEvent('expand', this);
46557         },
46558         
46559         restrictHeight : function()
46560         {
46561             this.list.alignTo(this.inputEl(), this.listAlign);
46562             this.list.alignTo(this.inputEl(), this.listAlign);
46563         },
46564         
46565         onViewOver : function(e, t)
46566         {
46567             if(this.inKeyMode){
46568                 return;
46569             }
46570             var item = this.view.findItemFromChild(t);
46571             
46572             if(item){
46573                 var index = this.view.indexOf(item);
46574                 this.select(index, false);
46575             }
46576         },
46577
46578         // private
46579         onViewClick : function(view, doFocus, el, e)
46580         {
46581             var index = this.view.getSelectedIndexes()[0];
46582             
46583             var r = this.store.getAt(index);
46584             
46585             if(r){
46586                 this.onSelect(r, index);
46587             }
46588             if(doFocus !== false && !this.blockFocus){
46589                 this.inputEl().focus();
46590             }
46591         },
46592         
46593         onViewMove : function(e, t)
46594         {
46595             this.inKeyMode = false;
46596         },
46597         
46598         select : function(index, scrollIntoView)
46599         {
46600             this.selectedIndex = index;
46601             this.view.select(index);
46602             if(scrollIntoView !== false){
46603                 var el = this.view.getNode(index);
46604                 if(el){
46605                     this.list.scrollChildIntoView(el, false);
46606                 }
46607             }
46608         },
46609         
46610         createList : function()
46611         {
46612             this.list = Roo.get(document.body).createChild({
46613                 tag: 'ul',
46614                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46615                 style: 'display:none'
46616             });
46617             
46618             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46619         },
46620         
46621         collapseIf : function(e)
46622         {
46623             var in_combo  = e.within(this.el);
46624             var in_list =  e.within(this.list);
46625             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46626             
46627             if (in_combo || in_list || is_list) {
46628                 return;
46629             }
46630             this.collapse();
46631         },
46632         
46633         onSelect : function(record, index)
46634         {
46635             if(this.fireEvent('beforeselect', this, record, index) !== false){
46636                 
46637                 this.setFlagClass(record.data.iso2);
46638                 this.setDialCode(record.data.dialCode);
46639                 this.hasFocus = false;
46640                 this.collapse();
46641                 this.fireEvent('select', this, record, index);
46642             }
46643         },
46644         
46645         flagEl : function()
46646         {
46647             var flag = this.el.select('div.flag',true).first();
46648             if(!flag){
46649                 return false;
46650             }
46651             return flag;
46652         },
46653         
46654         dialCodeHolderEl : function()
46655         {
46656             var d = this.el.select('input.dial-code-holder',true).first();
46657             if(!d){
46658                 return false;
46659             }
46660             return d;
46661         },
46662         
46663         setDialCode : function(v)
46664         {
46665             this.dialCodeHolder.dom.value = '+'+v;
46666         },
46667         
46668         setFlagClass : function(n)
46669         {
46670             this.flag.dom.className = 'flag '+n;
46671         },
46672         
46673         getValue : function()
46674         {
46675             var v = this.inputEl().getValue();
46676             if(this.dialCodeHolder) {
46677                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46678             }
46679             return v;
46680         },
46681         
46682         setValue : function(v)
46683         {
46684             var d = this.getDialCode(v);
46685             
46686             //invalid dial code
46687             if(v.length == 0 || !d || d.length == 0) {
46688                 if(this.rendered){
46689                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46690                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46691                 }
46692                 return;
46693             }
46694             
46695             //valid dial code
46696             this.setFlagClass(this.dialCodeMapping[d].iso2);
46697             this.setDialCode(d);
46698             this.inputEl().dom.value = v.replace('+'+d,'');
46699             this.hiddenEl().dom.value = this.getValue();
46700             
46701             this.validate();
46702         },
46703         
46704         getDialCode : function(v)
46705         {
46706             v = v ||  '';
46707             
46708             if (v.length == 0) {
46709                 return this.dialCodeHolder.dom.value;
46710             }
46711             
46712             var dialCode = "";
46713             if (v.charAt(0) != "+") {
46714                 return false;
46715             }
46716             var numericChars = "";
46717             for (var i = 1; i < v.length; i++) {
46718               var c = v.charAt(i);
46719               if (!isNaN(c)) {
46720                 numericChars += c;
46721                 if (this.dialCodeMapping[numericChars]) {
46722                   dialCode = v.substr(1, i);
46723                 }
46724                 if (numericChars.length == 4) {
46725                   break;
46726                 }
46727               }
46728             }
46729             return dialCode;
46730         },
46731         
46732         reset : function()
46733         {
46734             this.setValue(this.defaultDialCode);
46735             this.validate();
46736         },
46737         
46738         hiddenEl : function()
46739         {
46740             return this.el.select('input.hidden-tel-input',true).first();
46741         },
46742         
46743         // after setting val
46744         onKeyUp : function(e){
46745             this.setValue(this.getValue());
46746         },
46747         
46748         onKeyPress : function(e){
46749             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46750                 e.stopEvent();
46751             }
46752         }
46753         
46754 });
46755 /**
46756  * @class Roo.bootstrap.form.MoneyField
46757  * @extends Roo.bootstrap.form.ComboBox
46758  * Bootstrap MoneyField class
46759  * 
46760  * @constructor
46761  * Create a new MoneyField.
46762  * @param {Object} config Configuration options
46763  */
46764
46765 Roo.bootstrap.form.MoneyField = function(config) {
46766     
46767     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46768     
46769 };
46770
46771 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46772     
46773     /**
46774      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46775      */
46776     allowDecimals : true,
46777     /**
46778      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46779      */
46780     decimalSeparator : ".",
46781     /**
46782      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46783      */
46784     decimalPrecision : 0,
46785     /**
46786      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46787      */
46788     allowNegative : true,
46789     /**
46790      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46791      */
46792     allowZero: true,
46793     /**
46794      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46795      */
46796     minValue : Number.NEGATIVE_INFINITY,
46797     /**
46798      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46799      */
46800     maxValue : Number.MAX_VALUE,
46801     /**
46802      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46803      */
46804     minText : "The minimum value for this field is {0}",
46805     /**
46806      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46807      */
46808     maxText : "The maximum value for this field is {0}",
46809     /**
46810      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46811      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46812      */
46813     nanText : "{0} is not a valid number",
46814     /**
46815      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46816      */
46817     castInt : true,
46818     /**
46819      * @cfg {String} defaults currency of the MoneyField
46820      * value should be in lkey
46821      */
46822     defaultCurrency : false,
46823     /**
46824      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46825      */
46826     thousandsDelimiter : false,
46827     /**
46828      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46829      */
46830     max_length: false,
46831     
46832     inputlg : 9,
46833     inputmd : 9,
46834     inputsm : 9,
46835     inputxs : 6,
46836      /**
46837      * @cfg {Roo.data.Store} store  Store to lookup currency??
46838      */
46839     store : false,
46840     
46841     getAutoCreate : function()
46842     {
46843         var align = this.labelAlign || this.parentLabelAlign();
46844         
46845         var id = Roo.id();
46846
46847         var cfg = {
46848             cls: 'form-group',
46849             cn: []
46850         };
46851
46852         var input =  {
46853             tag: 'input',
46854             id : id,
46855             cls : 'form-control roo-money-amount-input',
46856             autocomplete: 'new-password'
46857         };
46858         
46859         var hiddenInput = {
46860             tag: 'input',
46861             type: 'hidden',
46862             id: Roo.id(),
46863             cls: 'hidden-number-input'
46864         };
46865         
46866         if(this.max_length) {
46867             input.maxlength = this.max_length; 
46868         }
46869         
46870         if (this.name) {
46871             hiddenInput.name = this.name;
46872         }
46873
46874         if (this.disabled) {
46875             input.disabled = true;
46876         }
46877
46878         var clg = 12 - this.inputlg;
46879         var cmd = 12 - this.inputmd;
46880         var csm = 12 - this.inputsm;
46881         var cxs = 12 - this.inputxs;
46882         
46883         var container = {
46884             tag : 'div',
46885             cls : 'row roo-money-field',
46886             cn : [
46887                 {
46888                     tag : 'div',
46889                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46890                     cn : [
46891                         {
46892                             tag : 'div',
46893                             cls: 'roo-select2-container input-group',
46894                             cn: [
46895                                 {
46896                                     tag : 'input',
46897                                     cls : 'form-control roo-money-currency-input',
46898                                     autocomplete: 'new-password',
46899                                     readOnly : 1,
46900                                     name : this.currencyName
46901                                 },
46902                                 {
46903                                     tag :'span',
46904                                     cls : 'input-group-addon',
46905                                     cn : [
46906                                         {
46907                                             tag: 'span',
46908                                             cls: 'caret'
46909                                         }
46910                                     ]
46911                                 }
46912                             ]
46913                         }
46914                     ]
46915                 },
46916                 {
46917                     tag : 'div',
46918                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46919                     cn : [
46920                         {
46921                             tag: 'div',
46922                             cls: this.hasFeedback ? 'has-feedback' : '',
46923                             cn: [
46924                                 input
46925                             ]
46926                         }
46927                     ]
46928                 }
46929             ]
46930             
46931         };
46932         
46933         if (this.fieldLabel.length) {
46934             var indicator = {
46935                 tag: 'i',
46936                 tooltip: 'This field is required'
46937             };
46938
46939             var label = {
46940                 tag: 'label',
46941                 'for':  id,
46942                 cls: 'control-label',
46943                 cn: []
46944             };
46945
46946             var label_text = {
46947                 tag: 'span',
46948                 html: this.fieldLabel
46949             };
46950
46951             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46952             label.cn = [
46953                 indicator,
46954                 label_text
46955             ];
46956
46957             if(this.indicatorpos == 'right') {
46958                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46959                 label.cn = [
46960                     label_text,
46961                     indicator
46962                 ];
46963             }
46964
46965             if(align == 'left') {
46966                 container = {
46967                     tag: 'div',
46968                     cn: [
46969                         container
46970                     ]
46971                 };
46972
46973                 if(this.labelWidth > 12){
46974                     label.style = "width: " + this.labelWidth + 'px';
46975                 }
46976                 if(this.labelWidth < 13 && this.labelmd == 0){
46977                     this.labelmd = this.labelWidth;
46978                 }
46979                 if(this.labellg > 0){
46980                     label.cls += ' col-lg-' + this.labellg;
46981                     input.cls += ' col-lg-' + (12 - this.labellg);
46982                 }
46983                 if(this.labelmd > 0){
46984                     label.cls += ' col-md-' + this.labelmd;
46985                     container.cls += ' col-md-' + (12 - this.labelmd);
46986                 }
46987                 if(this.labelsm > 0){
46988                     label.cls += ' col-sm-' + this.labelsm;
46989                     container.cls += ' col-sm-' + (12 - this.labelsm);
46990                 }
46991                 if(this.labelxs > 0){
46992                     label.cls += ' col-xs-' + this.labelxs;
46993                     container.cls += ' col-xs-' + (12 - this.labelxs);
46994                 }
46995             }
46996         }
46997
46998         cfg.cn = [
46999             label,
47000             container,
47001             hiddenInput
47002         ];
47003         
47004         var settings = this;
47005
47006         ['xs','sm','md','lg'].map(function(size){
47007             if (settings[size]) {
47008                 cfg.cls += ' col-' + size + '-' + settings[size];
47009             }
47010         });
47011         
47012         return cfg;
47013     },
47014     
47015     initEvents : function()
47016     {
47017         this.indicator = this.indicatorEl();
47018         
47019         this.initCurrencyEvent();
47020         
47021         this.initNumberEvent();
47022     },
47023     
47024     initCurrencyEvent : function()
47025     {
47026         if (!this.store) {
47027             throw "can not find store for combo";
47028         }
47029         
47030         this.store = Roo.factory(this.store, Roo.data);
47031         this.store.parent = this;
47032         
47033         this.createList();
47034         
47035         this.triggerEl = this.el.select('.input-group-addon', true).first();
47036         
47037         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47038         
47039         var _this = this;
47040         
47041         (function(){
47042             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47043             _this.list.setWidth(lw);
47044         }).defer(100);
47045         
47046         this.list.on('mouseover', this.onViewOver, this);
47047         this.list.on('mousemove', this.onViewMove, this);
47048         this.list.on('scroll', this.onViewScroll, this);
47049         
47050         if(!this.tpl){
47051             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47052         }
47053         
47054         this.view = new Roo.View(this.list, this.tpl, {
47055             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47056         });
47057         
47058         this.view.on('click', this.onViewClick, this);
47059         
47060         this.store.on('beforeload', this.onBeforeLoad, this);
47061         this.store.on('load', this.onLoad, this);
47062         this.store.on('loadexception', this.onLoadException, this);
47063         
47064         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47065             "up" : function(e){
47066                 this.inKeyMode = true;
47067                 this.selectPrev();
47068             },
47069
47070             "down" : function(e){
47071                 if(!this.isExpanded()){
47072                     this.onTriggerClick();
47073                 }else{
47074                     this.inKeyMode = true;
47075                     this.selectNext();
47076                 }
47077             },
47078
47079             "enter" : function(e){
47080                 this.collapse();
47081                 
47082                 if(this.fireEvent("specialkey", this, e)){
47083                     this.onViewClick(false);
47084                 }
47085                 
47086                 return true;
47087             },
47088
47089             "esc" : function(e){
47090                 this.collapse();
47091             },
47092
47093             "tab" : function(e){
47094                 this.collapse();
47095                 
47096                 if(this.fireEvent("specialkey", this, e)){
47097                     this.onViewClick(false);
47098                 }
47099                 
47100                 return true;
47101             },
47102
47103             scope : this,
47104
47105             doRelay : function(foo, bar, hname){
47106                 if(hname == 'down' || this.scope.isExpanded()){
47107                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47108                 }
47109                 return true;
47110             },
47111
47112             forceKeyDown: true
47113         });
47114         
47115         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47116         
47117     },
47118     
47119     initNumberEvent : function(e)
47120     {
47121         this.inputEl().on("keydown" , this.fireKey,  this);
47122         this.inputEl().on("focus", this.onFocus,  this);
47123         this.inputEl().on("blur", this.onBlur,  this);
47124         
47125         this.inputEl().relayEvent('keyup', this);
47126         
47127         if(this.indicator){
47128             this.indicator.addClass('invisible');
47129         }
47130  
47131         this.originalValue = this.getValue();
47132         
47133         if(this.validationEvent == 'keyup'){
47134             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47135             this.inputEl().on('keyup', this.filterValidation, this);
47136         }
47137         else if(this.validationEvent !== false){
47138             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47139         }
47140         
47141         if(this.selectOnFocus){
47142             this.on("focus", this.preFocus, this);
47143             
47144         }
47145         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47146             this.inputEl().on("keypress", this.filterKeys, this);
47147         } else {
47148             this.inputEl().relayEvent('keypress', this);
47149         }
47150         
47151         var allowed = "0123456789";
47152         
47153         if(this.allowDecimals){
47154             allowed += this.decimalSeparator;
47155         }
47156         
47157         if(this.allowNegative){
47158             allowed += "-";
47159         }
47160         
47161         if(this.thousandsDelimiter) {
47162             allowed += ",";
47163         }
47164         
47165         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47166         
47167         var keyPress = function(e){
47168             
47169             var k = e.getKey();
47170             
47171             var c = e.getCharCode();
47172             
47173             if(
47174                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47175                     allowed.indexOf(String.fromCharCode(c)) === -1
47176             ){
47177                 e.stopEvent();
47178                 return;
47179             }
47180             
47181             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47182                 return;
47183             }
47184             
47185             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47186                 e.stopEvent();
47187             }
47188         };
47189         
47190         this.inputEl().on("keypress", keyPress, this);
47191         
47192     },
47193     
47194     onTriggerClick : function(e)
47195     {   
47196         if(this.disabled){
47197             return;
47198         }
47199         
47200         this.page = 0;
47201         this.loadNext = false;
47202         
47203         if(this.isExpanded()){
47204             this.collapse();
47205             return;
47206         }
47207         
47208         this.hasFocus = true;
47209         
47210         if(this.triggerAction == 'all') {
47211             this.doQuery(this.allQuery, true);
47212             return;
47213         }
47214         
47215         this.doQuery(this.getRawValue());
47216     },
47217     
47218     getCurrency : function()
47219     {   
47220         var v = this.currencyEl().getValue();
47221         
47222         return v;
47223     },
47224     
47225     restrictHeight : function()
47226     {
47227         this.list.alignTo(this.currencyEl(), this.listAlign);
47228         this.list.alignTo(this.currencyEl(), this.listAlign);
47229     },
47230     
47231     onViewClick : function(view, doFocus, el, e)
47232     {
47233         var index = this.view.getSelectedIndexes()[0];
47234         
47235         var r = this.store.getAt(index);
47236         
47237         if(r){
47238             this.onSelect(r, index);
47239         }
47240     },
47241     
47242     onSelect : function(record, index){
47243         
47244         if(this.fireEvent('beforeselect', this, record, index) !== false){
47245         
47246             this.setFromCurrencyData(index > -1 ? record.data : false);
47247             
47248             this.collapse();
47249             
47250             this.fireEvent('select', this, record, index);
47251         }
47252     },
47253     
47254     setFromCurrencyData : function(o)
47255     {
47256         var currency = '';
47257         
47258         this.lastCurrency = o;
47259         
47260         if (this.currencyField) {
47261             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47262         } else {
47263             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47264         }
47265         
47266         this.lastSelectionText = currency;
47267         
47268         //setting default currency
47269         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47270             this.setCurrency(this.defaultCurrency);
47271             return;
47272         }
47273         
47274         this.setCurrency(currency);
47275     },
47276     
47277     setFromData : function(o)
47278     {
47279         var c = {};
47280         
47281         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47282         
47283         this.setFromCurrencyData(c);
47284         
47285         var value = '';
47286         
47287         if (this.name) {
47288             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47289         } else {
47290             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47291         }
47292         
47293         this.setValue(value);
47294         
47295     },
47296     
47297     setCurrency : function(v)
47298     {   
47299         this.currencyValue = v;
47300         
47301         if(this.rendered){
47302             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47303             this.validate();
47304         }
47305     },
47306     
47307     setValue : function(v)
47308     {
47309         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47310         
47311         this.value = v;
47312         
47313         if(this.rendered){
47314             
47315             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47316             
47317             this.inputEl().dom.value = (v == '') ? '' :
47318                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47319             
47320             if(!this.allowZero && v === '0') {
47321                 this.hiddenEl().dom.value = '';
47322                 this.inputEl().dom.value = '';
47323             }
47324             
47325             this.validate();
47326         }
47327     },
47328     
47329     getRawValue : function()
47330     {
47331         var v = this.inputEl().getValue();
47332         
47333         return v;
47334     },
47335     
47336     getValue : function()
47337     {
47338         return this.fixPrecision(this.parseValue(this.getRawValue()));
47339     },
47340     
47341     parseValue : function(value)
47342     {
47343         if(this.thousandsDelimiter) {
47344             value += "";
47345             r = new RegExp(",", "g");
47346             value = value.replace(r, "");
47347         }
47348         
47349         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47350         return isNaN(value) ? '' : value;
47351         
47352     },
47353     
47354     fixPrecision : function(value)
47355     {
47356         if(this.thousandsDelimiter) {
47357             value += "";
47358             r = new RegExp(",", "g");
47359             value = value.replace(r, "");
47360         }
47361         
47362         var nan = isNaN(value);
47363         
47364         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47365             return nan ? '' : value;
47366         }
47367         return parseFloat(value).toFixed(this.decimalPrecision);
47368     },
47369     
47370     decimalPrecisionFcn : function(v)
47371     {
47372         return Math.floor(v);
47373     },
47374     
47375     validateValue : function(value)
47376     {
47377         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47378             return false;
47379         }
47380         
47381         var num = this.parseValue(value);
47382         
47383         if(isNaN(num)){
47384             this.markInvalid(String.format(this.nanText, value));
47385             return false;
47386         }
47387         
47388         if(num < this.minValue){
47389             this.markInvalid(String.format(this.minText, this.minValue));
47390             return false;
47391         }
47392         
47393         if(num > this.maxValue){
47394             this.markInvalid(String.format(this.maxText, this.maxValue));
47395             return false;
47396         }
47397         
47398         return true;
47399     },
47400     
47401     validate : function()
47402     {
47403         if(this.disabled || this.allowBlank){
47404             this.markValid();
47405             return true;
47406         }
47407         
47408         var currency = this.getCurrency();
47409         
47410         if(this.validateValue(this.getRawValue()) && currency.length){
47411             this.markValid();
47412             return true;
47413         }
47414         
47415         this.markInvalid();
47416         return false;
47417     },
47418     
47419     getName: function()
47420     {
47421         return this.name;
47422     },
47423     
47424     beforeBlur : function()
47425     {
47426         if(!this.castInt){
47427             return;
47428         }
47429         
47430         var v = this.parseValue(this.getRawValue());
47431         
47432         if(v || v == 0){
47433             this.setValue(v);
47434         }
47435     },
47436     
47437     onBlur : function()
47438     {
47439         this.beforeBlur();
47440         
47441         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47442             //this.el.removeClass(this.focusClass);
47443         }
47444         
47445         this.hasFocus = false;
47446         
47447         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47448             this.validate();
47449         }
47450         
47451         var v = this.getValue();
47452         
47453         if(String(v) !== String(this.startValue)){
47454             this.fireEvent('change', this, v, this.startValue);
47455         }
47456         
47457         this.fireEvent("blur", this);
47458     },
47459     
47460     inputEl : function()
47461     {
47462         return this.el.select('.roo-money-amount-input', true).first();
47463     },
47464     
47465     currencyEl : function()
47466     {
47467         return this.el.select('.roo-money-currency-input', true).first();
47468     },
47469     
47470     hiddenEl : function()
47471     {
47472         return this.el.select('input.hidden-number-input',true).first();
47473     }
47474     
47475 });/**
47476  * @class Roo.bootstrap.BezierSignature
47477  * @extends Roo.bootstrap.Component
47478  * Bootstrap BezierSignature class
47479  * This script refer to:
47480  *    Title: Signature Pad
47481  *    Author: szimek
47482  *    Availability: https://github.com/szimek/signature_pad
47483  *
47484  * @constructor
47485  * Create a new BezierSignature
47486  * @param {Object} config The config object
47487  */
47488
47489 Roo.bootstrap.BezierSignature = function(config){
47490     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47491     this.addEvents({
47492         "resize" : true
47493     });
47494 };
47495
47496 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47497 {
47498      
47499     curve_data: [],
47500     
47501     is_empty: true,
47502     
47503     mouse_btn_down: true,
47504     
47505     /**
47506      * @cfg {int} canvas height
47507      */
47508     canvas_height: '200px',
47509     
47510     /**
47511      * @cfg {float|function} Radius of a single dot.
47512      */ 
47513     dot_size: false,
47514     
47515     /**
47516      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47517      */
47518     min_width: 0.5,
47519     
47520     /**
47521      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47522      */
47523     max_width: 2.5,
47524     
47525     /**
47526      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47527      */
47528     throttle: 16,
47529     
47530     /**
47531      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47532      */
47533     min_distance: 5,
47534     
47535     /**
47536      * @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.
47537      */
47538     bg_color: 'rgba(0, 0, 0, 0)',
47539     
47540     /**
47541      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47542      */
47543     dot_color: 'black',
47544     
47545     /**
47546      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47547      */ 
47548     velocity_filter_weight: 0.7,
47549     
47550     /**
47551      * @cfg {function} Callback when stroke begin. 
47552      */
47553     onBegin: false,
47554     
47555     /**
47556      * @cfg {function} Callback when stroke end.
47557      */
47558     onEnd: false,
47559     
47560     getAutoCreate : function()
47561     {
47562         var cls = 'roo-signature column';
47563         
47564         if(this.cls){
47565             cls += ' ' + this.cls;
47566         }
47567         
47568         var col_sizes = [
47569             'lg',
47570             'md',
47571             'sm',
47572             'xs'
47573         ];
47574         
47575         for(var i = 0; i < col_sizes.length; i++) {
47576             if(this[col_sizes[i]]) {
47577                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47578             }
47579         }
47580         
47581         var cfg = {
47582             tag: 'div',
47583             cls: cls,
47584             cn: [
47585                 {
47586                     tag: 'div',
47587                     cls: 'roo-signature-body',
47588                     cn: [
47589                         {
47590                             tag: 'canvas',
47591                             cls: 'roo-signature-body-canvas',
47592                             height: this.canvas_height,
47593                             width: this.canvas_width
47594                         }
47595                     ]
47596                 },
47597                 {
47598                     tag: 'input',
47599                     type: 'file',
47600                     style: 'display: none'
47601                 }
47602             ]
47603         };
47604         
47605         return cfg;
47606     },
47607     
47608     initEvents: function() 
47609     {
47610         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47611         
47612         var canvas = this.canvasEl();
47613         
47614         // mouse && touch event swapping...
47615         canvas.dom.style.touchAction = 'none';
47616         canvas.dom.style.msTouchAction = 'none';
47617         
47618         this.mouse_btn_down = false;
47619         canvas.on('mousedown', this._handleMouseDown, this);
47620         canvas.on('mousemove', this._handleMouseMove, this);
47621         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47622         
47623         if (window.PointerEvent) {
47624             canvas.on('pointerdown', this._handleMouseDown, this);
47625             canvas.on('pointermove', this._handleMouseMove, this);
47626             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47627         }
47628         
47629         if ('ontouchstart' in window) {
47630             canvas.on('touchstart', this._handleTouchStart, this);
47631             canvas.on('touchmove', this._handleTouchMove, this);
47632             canvas.on('touchend', this._handleTouchEnd, this);
47633         }
47634         
47635         Roo.EventManager.onWindowResize(this.resize, this, true);
47636         
47637         // file input event
47638         this.fileEl().on('change', this.uploadImage, this);
47639         
47640         this.clear();
47641         
47642         this.resize();
47643     },
47644     
47645     resize: function(){
47646         
47647         var canvas = this.canvasEl().dom;
47648         var ctx = this.canvasElCtx();
47649         var img_data = false;
47650         
47651         if(canvas.width > 0) {
47652             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47653         }
47654         // setting canvas width will clean img data
47655         canvas.width = 0;
47656         
47657         var style = window.getComputedStyle ? 
47658             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47659             
47660         var padding_left = parseInt(style.paddingLeft) || 0;
47661         var padding_right = parseInt(style.paddingRight) || 0;
47662         
47663         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47664         
47665         if(img_data) {
47666             ctx.putImageData(img_data, 0, 0);
47667         }
47668     },
47669     
47670     _handleMouseDown: function(e)
47671     {
47672         if (e.browserEvent.which === 1) {
47673             this.mouse_btn_down = true;
47674             this.strokeBegin(e);
47675         }
47676     },
47677     
47678     _handleMouseMove: function (e)
47679     {
47680         if (this.mouse_btn_down) {
47681             this.strokeMoveUpdate(e);
47682         }
47683     },
47684     
47685     _handleMouseUp: function (e)
47686     {
47687         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47688             this.mouse_btn_down = false;
47689             this.strokeEnd(e);
47690         }
47691     },
47692     
47693     _handleTouchStart: function (e) {
47694         
47695         e.preventDefault();
47696         if (e.browserEvent.targetTouches.length === 1) {
47697             // var touch = e.browserEvent.changedTouches[0];
47698             // this.strokeBegin(touch);
47699             
47700              this.strokeBegin(e); // assume e catching the correct xy...
47701         }
47702     },
47703     
47704     _handleTouchMove: function (e) {
47705         e.preventDefault();
47706         // var touch = event.targetTouches[0];
47707         // _this._strokeMoveUpdate(touch);
47708         this.strokeMoveUpdate(e);
47709     },
47710     
47711     _handleTouchEnd: function (e) {
47712         var wasCanvasTouched = e.target === this.canvasEl().dom;
47713         if (wasCanvasTouched) {
47714             e.preventDefault();
47715             // var touch = event.changedTouches[0];
47716             // _this._strokeEnd(touch);
47717             this.strokeEnd(e);
47718         }
47719     },
47720     
47721     reset: function () {
47722         this._lastPoints = [];
47723         this._lastVelocity = 0;
47724         this._lastWidth = (this.min_width + this.max_width) / 2;
47725         this.canvasElCtx().fillStyle = this.dot_color;
47726     },
47727     
47728     strokeMoveUpdate: function(e)
47729     {
47730         this.strokeUpdate(e);
47731         
47732         if (this.throttle) {
47733             this.throttleStroke(this.strokeUpdate, this.throttle);
47734         }
47735         else {
47736             this.strokeUpdate(e);
47737         }
47738     },
47739     
47740     strokeBegin: function(e)
47741     {
47742         var newPointGroup = {
47743             color: this.dot_color,
47744             points: []
47745         };
47746         
47747         if (typeof this.onBegin === 'function') {
47748             this.onBegin(e);
47749         }
47750         
47751         this.curve_data.push(newPointGroup);
47752         this.reset();
47753         this.strokeUpdate(e);
47754     },
47755     
47756     strokeUpdate: function(e)
47757     {
47758         var rect = this.canvasEl().dom.getBoundingClientRect();
47759         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47760         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47761         var lastPoints = lastPointGroup.points;
47762         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47763         var isLastPointTooClose = lastPoint
47764             ? point.distanceTo(lastPoint) <= this.min_distance
47765             : false;
47766         var color = lastPointGroup.color;
47767         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47768             var curve = this.addPoint(point);
47769             if (!lastPoint) {
47770                 this.drawDot({color: color, point: point});
47771             }
47772             else if (curve) {
47773                 this.drawCurve({color: color, curve: curve});
47774             }
47775             lastPoints.push({
47776                 time: point.time,
47777                 x: point.x,
47778                 y: point.y
47779             });
47780         }
47781     },
47782     
47783     strokeEnd: function(e)
47784     {
47785         this.strokeUpdate(e);
47786         if (typeof this.onEnd === 'function') {
47787             this.onEnd(e);
47788         }
47789     },
47790     
47791     addPoint:  function (point) {
47792         var _lastPoints = this._lastPoints;
47793         _lastPoints.push(point);
47794         if (_lastPoints.length > 2) {
47795             if (_lastPoints.length === 3) {
47796                 _lastPoints.unshift(_lastPoints[0]);
47797             }
47798             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47799             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47800             _lastPoints.shift();
47801             return curve;
47802         }
47803         return null;
47804     },
47805     
47806     calculateCurveWidths: function (startPoint, endPoint) {
47807         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47808             (1 - this.velocity_filter_weight) * this._lastVelocity;
47809
47810         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47811         var widths = {
47812             end: newWidth,
47813             start: this._lastWidth
47814         };
47815         
47816         this._lastVelocity = velocity;
47817         this._lastWidth = newWidth;
47818         return widths;
47819     },
47820     
47821     drawDot: function (_a) {
47822         var color = _a.color, point = _a.point;
47823         var ctx = this.canvasElCtx();
47824         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47825         ctx.beginPath();
47826         this.drawCurveSegment(point.x, point.y, width);
47827         ctx.closePath();
47828         ctx.fillStyle = color;
47829         ctx.fill();
47830     },
47831     
47832     drawCurve: function (_a) {
47833         var color = _a.color, curve = _a.curve;
47834         var ctx = this.canvasElCtx();
47835         var widthDelta = curve.endWidth - curve.startWidth;
47836         var drawSteps = Math.floor(curve.length()) * 2;
47837         ctx.beginPath();
47838         ctx.fillStyle = color;
47839         for (var i = 0; i < drawSteps; i += 1) {
47840         var t = i / drawSteps;
47841         var tt = t * t;
47842         var ttt = tt * t;
47843         var u = 1 - t;
47844         var uu = u * u;
47845         var uuu = uu * u;
47846         var x = uuu * curve.startPoint.x;
47847         x += 3 * uu * t * curve.control1.x;
47848         x += 3 * u * tt * curve.control2.x;
47849         x += ttt * curve.endPoint.x;
47850         var y = uuu * curve.startPoint.y;
47851         y += 3 * uu * t * curve.control1.y;
47852         y += 3 * u * tt * curve.control2.y;
47853         y += ttt * curve.endPoint.y;
47854         var width = curve.startWidth + ttt * widthDelta;
47855         this.drawCurveSegment(x, y, width);
47856         }
47857         ctx.closePath();
47858         ctx.fill();
47859     },
47860     
47861     drawCurveSegment: function (x, y, width) {
47862         var ctx = this.canvasElCtx();
47863         ctx.moveTo(x, y);
47864         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47865         this.is_empty = false;
47866     },
47867     
47868     clear: function()
47869     {
47870         var ctx = this.canvasElCtx();
47871         var canvas = this.canvasEl().dom;
47872         ctx.fillStyle = this.bg_color;
47873         ctx.clearRect(0, 0, canvas.width, canvas.height);
47874         ctx.fillRect(0, 0, canvas.width, canvas.height);
47875         this.curve_data = [];
47876         this.reset();
47877         this.is_empty = true;
47878     },
47879     
47880     fileEl: function()
47881     {
47882         return  this.el.select('input',true).first();
47883     },
47884     
47885     canvasEl: function()
47886     {
47887         return this.el.select('canvas',true).first();
47888     },
47889     
47890     canvasElCtx: function()
47891     {
47892         return this.el.select('canvas',true).first().dom.getContext('2d');
47893     },
47894     
47895     getImage: function(type)
47896     {
47897         if(this.is_empty) {
47898             return false;
47899         }
47900         
47901         // encryption ?
47902         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47903     },
47904     
47905     drawFromImage: function(img_src)
47906     {
47907         var img = new Image();
47908         
47909         img.onload = function(){
47910             this.canvasElCtx().drawImage(img, 0, 0);
47911         }.bind(this);
47912         
47913         img.src = img_src;
47914         
47915         this.is_empty = false;
47916     },
47917     
47918     selectImage: function()
47919     {
47920         this.fileEl().dom.click();
47921     },
47922     
47923     uploadImage: function(e)
47924     {
47925         var reader = new FileReader();
47926         
47927         reader.onload = function(e){
47928             var img = new Image();
47929             img.onload = function(){
47930                 this.reset();
47931                 this.canvasElCtx().drawImage(img, 0, 0);
47932             }.bind(this);
47933             img.src = e.target.result;
47934         }.bind(this);
47935         
47936         reader.readAsDataURL(e.target.files[0]);
47937     },
47938     
47939     // Bezier Point Constructor
47940     Point: (function () {
47941         function Point(x, y, time) {
47942             this.x = x;
47943             this.y = y;
47944             this.time = time || Date.now();
47945         }
47946         Point.prototype.distanceTo = function (start) {
47947             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47948         };
47949         Point.prototype.equals = function (other) {
47950             return this.x === other.x && this.y === other.y && this.time === other.time;
47951         };
47952         Point.prototype.velocityFrom = function (start) {
47953             return this.time !== start.time
47954             ? this.distanceTo(start) / (this.time - start.time)
47955             : 0;
47956         };
47957         return Point;
47958     }()),
47959     
47960     
47961     // Bezier Constructor
47962     Bezier: (function () {
47963         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47964             this.startPoint = startPoint;
47965             this.control2 = control2;
47966             this.control1 = control1;
47967             this.endPoint = endPoint;
47968             this.startWidth = startWidth;
47969             this.endWidth = endWidth;
47970         }
47971         Bezier.fromPoints = function (points, widths, scope) {
47972             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47973             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47974             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47975         };
47976         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47977             var dx1 = s1.x - s2.x;
47978             var dy1 = s1.y - s2.y;
47979             var dx2 = s2.x - s3.x;
47980             var dy2 = s2.y - s3.y;
47981             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47982             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47983             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47984             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47985             var dxm = m1.x - m2.x;
47986             var dym = m1.y - m2.y;
47987             var k = l2 / (l1 + l2);
47988             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47989             var tx = s2.x - cm.x;
47990             var ty = s2.y - cm.y;
47991             return {
47992                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47993                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47994             };
47995         };
47996         Bezier.prototype.length = function () {
47997             var steps = 10;
47998             var length = 0;
47999             var px;
48000             var py;
48001             for (var i = 0; i <= steps; i += 1) {
48002                 var t = i / steps;
48003                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
48004                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
48005                 if (i > 0) {
48006                     var xdiff = cx - px;
48007                     var ydiff = cy - py;
48008                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48009                 }
48010                 px = cx;
48011                 py = cy;
48012             }
48013             return length;
48014         };
48015         Bezier.prototype.point = function (t, start, c1, c2, end) {
48016             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48017             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48018             + (3.0 * c2 * (1.0 - t) * t * t)
48019             + (end * t * t * t);
48020         };
48021         return Bezier;
48022     }()),
48023     
48024     throttleStroke: function(fn, wait) {
48025       if (wait === void 0) { wait = 250; }
48026       var previous = 0;
48027       var timeout = null;
48028       var result;
48029       var storedContext;
48030       var storedArgs;
48031       var later = function () {
48032           previous = Date.now();
48033           timeout = null;
48034           result = fn.apply(storedContext, storedArgs);
48035           if (!timeout) {
48036               storedContext = null;
48037               storedArgs = [];
48038           }
48039       };
48040       return function wrapper() {
48041           var args = [];
48042           for (var _i = 0; _i < arguments.length; _i++) {
48043               args[_i] = arguments[_i];
48044           }
48045           var now = Date.now();
48046           var remaining = wait - (now - previous);
48047           storedContext = this;
48048           storedArgs = args;
48049           if (remaining <= 0 || remaining > wait) {
48050               if (timeout) {
48051                   clearTimeout(timeout);
48052                   timeout = null;
48053               }
48054               previous = now;
48055               result = fn.apply(storedContext, storedArgs);
48056               if (!timeout) {
48057                   storedContext = null;
48058                   storedArgs = [];
48059               }
48060           }
48061           else if (!timeout) {
48062               timeout = window.setTimeout(later, remaining);
48063           }
48064           return result;
48065       };
48066   }
48067   
48068 });
48069
48070  
48071
48072  // old names for form elements
48073 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48074 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48075 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48076 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48077 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48078 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48079 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48080 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48081 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48082 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48083 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48084 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48085 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48086 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48087 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48088 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48089 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48090 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48091 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48092 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48093 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48094 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48095 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48096 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48097 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48098 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48099
48100 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48101 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48102
48103 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48104 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48105
48106 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48107 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48108 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48109 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48110