Roo/bootstrap/Table.js
[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 auto size
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         
9509         
9510         var cm = this.cm, styles = [];
9511         this.CSS.removeStyleSheet(this.id + '-cssrules');
9512         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9513         // we can honour xs/sm/md/xl  as widths...
9514         // we first have to decide what widht we are currently at...
9515         var sz = Roo.getGridSize();
9516         
9517         var total = 0;
9518         var last = -1;
9519         var cols = []; // visable cols.
9520         var total_abs = 0;
9521         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9522             var w = cm.getColumnWidth(i, false);
9523             if(cm.isHidden(i)){
9524                 cols.push( { rel : false, abs : 0 });
9525                 continue;
9526             }
9527             if (w !== false) {
9528                 cols.push( { rel : false, abs : w });
9529                 total_abs += w;
9530                 last = i; // not really..
9531                 continue;
9532             }
9533             var w = cm.getColumnWidth(i, sz);
9534             if (w > 0) {
9535                 last = i
9536             }
9537             total += w;
9538             cols.push( { rel : w, abs : false });
9539         }
9540         
9541         var avail = this.bodyEl.dom.clientWidth - total_abs;
9542         
9543         var unitWidth = Math.floor(avail / total);
9544         var rem = avail - (unitWidth * total);
9545         
9546         var hidden, width, pos = 0 , splithide , left;
9547         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9548             
9549             hidden = 'display:none;';
9550             left = '';
9551             width  = 'width:0px;';
9552             splithide = '';
9553             if(!cm.isHidden(i)){
9554                 hidden = '';
9555                 
9556                 
9557                 // we can honour xs/sm/md/xl ?
9558                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9559                 if (w===0) {
9560                     hidden = 'display:none;';
9561                 }
9562                 // width should return a small number...
9563                 if (i == last) {
9564                     w+=rem; // add the remaining with..
9565                 }
9566                 pos += w;
9567                 left = "left:" + (pos -4) + "px;";
9568                 width = "width:" + w+ "px;";
9569                 
9570             }
9571             if (this.responsive) {
9572                 width = '';
9573                 left = '';
9574                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9575                 splithide = 'display: none;';
9576             }
9577             
9578             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9579             if (this.headEl) {
9580                 if (i == last) {
9581                     splithide = 'display:none;';
9582                 }
9583                 
9584                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9585                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9586                             // this is the popover version..
9587                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9588                 );
9589             }
9590             
9591         }
9592         //Roo.log(styles.join(''));
9593         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9594         
9595     },
9596     
9597     
9598     
9599     onContextMenu : function(e, t)
9600     {
9601         this.processEvent("contextmenu", e);
9602     },
9603     
9604     processEvent : function(name, e)
9605     {
9606         if (name != 'touchstart' ) {
9607             this.fireEvent(name, e);    
9608         }
9609         
9610         var t = e.getTarget();
9611         
9612         var cell = Roo.get(t);
9613         
9614         if(!cell){
9615             return;
9616         }
9617         
9618         if(cell.findParent('tfoot', false, true)){
9619             return;
9620         }
9621         
9622         if(cell.findParent('thead', false, true)){
9623             
9624             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9625                 cell = Roo.get(t).findParent('th', false, true);
9626                 if (!cell) {
9627                     Roo.log("failed to find th in thead?");
9628                     Roo.log(e.getTarget());
9629                     return;
9630                 }
9631             }
9632             
9633             var cellIndex = cell.dom.cellIndex;
9634             
9635             var ename = name == 'touchstart' ? 'click' : name;
9636             this.fireEvent("header" + ename, this, cellIndex, e);
9637             
9638             return;
9639         }
9640         
9641         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9642             cell = Roo.get(t).findParent('td', false, true);
9643             if (!cell) {
9644                 Roo.log("failed to find th in tbody?");
9645                 Roo.log(e.getTarget());
9646                 return;
9647             }
9648         }
9649         
9650         var row = cell.findParent('tr', false, true);
9651         var cellIndex = cell.dom.cellIndex;
9652         var rowIndex = row.dom.rowIndex - 1;
9653         
9654         if(row !== false){
9655             
9656             this.fireEvent("row" + name, this, rowIndex, e);
9657             
9658             if(cell !== false){
9659             
9660                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9661             }
9662         }
9663         
9664     },
9665     
9666     onMouseover : function(e, el)
9667     {
9668         var cell = Roo.get(el);
9669         
9670         if(!cell){
9671             return;
9672         }
9673         
9674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9675             cell = cell.findParent('td', false, true);
9676         }
9677         
9678         var row = cell.findParent('tr', false, true);
9679         var cellIndex = cell.dom.cellIndex;
9680         var rowIndex = row.dom.rowIndex - 1; // start from 0
9681         
9682         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9683         
9684     },
9685     
9686     onMouseout : function(e, el)
9687     {
9688         var cell = Roo.get(el);
9689         
9690         if(!cell){
9691             return;
9692         }
9693         
9694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9695             cell = cell.findParent('td', false, true);
9696         }
9697         
9698         var row = cell.findParent('tr', false, true);
9699         var cellIndex = cell.dom.cellIndex;
9700         var rowIndex = row.dom.rowIndex - 1; // start from 0
9701         
9702         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9703         
9704     },
9705     
9706     onClick : function(e, el)
9707     {
9708         var cell = Roo.get(el);
9709         
9710         if(!cell || (!this.cellSelection && !this.rowSelection)){
9711             return;
9712         }
9713         
9714         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9715             cell = cell.findParent('td', false, true);
9716         }
9717         
9718         if(!cell || typeof(cell) == 'undefined'){
9719             return;
9720         }
9721         
9722         var row = cell.findParent('tr', false, true);
9723         
9724         if(!row || typeof(row) == 'undefined'){
9725             return;
9726         }
9727         
9728         var cellIndex = cell.dom.cellIndex;
9729         var rowIndex = this.getRowIndex(row);
9730         
9731         // why??? - should these not be based on SelectionModel?
9732         //if(this.cellSelection){
9733             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9734         //}
9735         
9736         //if(this.rowSelection){
9737             this.fireEvent('rowclick', this, row, rowIndex, e);
9738         //}
9739          
9740     },
9741         
9742     onDblClick : function(e,el)
9743     {
9744         var cell = Roo.get(el);
9745         
9746         if(!cell || (!this.cellSelection && !this.rowSelection)){
9747             return;
9748         }
9749         
9750         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9751             cell = cell.findParent('td', false, true);
9752         }
9753         
9754         if(!cell || typeof(cell) == 'undefined'){
9755             return;
9756         }
9757         
9758         var row = cell.findParent('tr', false, true);
9759         
9760         if(!row || typeof(row) == 'undefined'){
9761             return;
9762         }
9763         
9764         var cellIndex = cell.dom.cellIndex;
9765         var rowIndex = this.getRowIndex(row);
9766         
9767         if(this.cellSelection){
9768             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9769         }
9770         
9771         if(this.rowSelection){
9772             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9773         }
9774     },
9775     findRowIndex : function(el)
9776     {
9777         var cell = Roo.get(el);
9778         if(!cell) {
9779             return false;
9780         }
9781         var row = cell.findParent('tr', false, true);
9782         
9783         if(!row || typeof(row) == 'undefined'){
9784             return false;
9785         }
9786         return this.getRowIndex(row);
9787     },
9788     sort : function(e,el)
9789     {
9790         var col = Roo.get(el);
9791         
9792         if(!col.hasClass('sortable')){
9793             return;
9794         }
9795         
9796         var sort = col.attr('sort');
9797         var dir = 'ASC';
9798         
9799         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9800             dir = 'DESC';
9801         }
9802         
9803         this.store.sortInfo = {field : sort, direction : dir};
9804         
9805         if (this.footer) {
9806             Roo.log("calling footer first");
9807             this.footer.onClick('first');
9808         } else {
9809         
9810             this.store.load({ params : { start : 0 } });
9811         }
9812     },
9813     
9814     renderHeader : function()
9815     {
9816         var header = {
9817             tag: 'thead',
9818             cn : []
9819         };
9820         
9821         var cm = this.cm;
9822         this.totalWidth = 0;
9823         
9824         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9825             
9826             var config = cm.config[i];
9827             
9828             var c = {
9829                 tag: 'th',
9830                 cls : 'x-hcol-' + i,
9831                 style : '',
9832                 
9833                 html: cm.getColumnHeader(i)
9834             };
9835             
9836             var tooltip = cm.getColumnTooltip(i);
9837             if (tooltip) {
9838                 c.tooltip = tooltip;
9839             }
9840             
9841             
9842             var hh = '';
9843             
9844             if(typeof(config.sortable) != 'undefined' && config.sortable){
9845                 c.cls += ' sortable';
9846                 c.html = '<i class="fa"></i>' + c.html;
9847             }
9848             
9849             // could use BS4 hidden-..-down 
9850             
9851             if(typeof(config.lgHeader) != 'undefined'){
9852                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9853             }
9854             
9855             if(typeof(config.mdHeader) != 'undefined'){
9856                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9857             }
9858             
9859             if(typeof(config.smHeader) != 'undefined'){
9860                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9861             }
9862             
9863             if(typeof(config.xsHeader) != 'undefined'){
9864                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9865             }
9866             
9867             if(hh.length){
9868                 c.html = hh;
9869             }
9870             
9871             if(typeof(config.tooltip) != 'undefined'){
9872                 c.tooltip = config.tooltip;
9873             }
9874             
9875             if(typeof(config.colspan) != 'undefined'){
9876                 c.colspan = config.colspan;
9877             }
9878             
9879             // hidden is handled by CSS now
9880             
9881             if(typeof(config.dataIndex) != 'undefined'){
9882                 c.sort = config.dataIndex;
9883             }
9884             
9885            
9886             
9887             if(typeof(config.align) != 'undefined' && config.align.length){
9888                 c.style += ' text-align:' + config.align + ';';
9889             }
9890             
9891             /* width is done in CSS
9892              *if(typeof(config.width) != 'undefined'){
9893                 c.style += ' width:' + config.width + 'px;';
9894                 this.totalWidth += config.width;
9895             } else {
9896                 this.totalWidth += 100; // assume minimum of 100 per column?
9897             }
9898             */
9899             
9900             if(typeof(config.cls) != 'undefined'){
9901                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9902             }
9903             // this is the bit that doesnt reall work at all...
9904             
9905             if (this.responsive) {
9906                  
9907             
9908                 ['xs','sm','md','lg'].map(function(size){
9909                     
9910                     if(typeof(config[size]) == 'undefined'){
9911                         return;
9912                     }
9913                      
9914                     if (!config[size]) { // 0 = hidden
9915                         // BS 4 '0' is treated as hide that column and below.
9916                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9917                         return;
9918                     }
9919                     
9920                     c.cls += ' col-' + size + '-' + config[size] + (
9921                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9922                     );
9923                     
9924                     
9925                 });
9926             }
9927             // at the end?
9928             
9929             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9930             
9931             
9932             
9933             
9934             header.cn.push(c)
9935         }
9936         
9937         return header;
9938     },
9939     
9940     renderBody : function()
9941     {
9942         var body = {
9943             tag: 'tbody',
9944             cn : [
9945                 {
9946                     tag: 'tr',
9947                     cn : [
9948                         {
9949                             tag : 'td',
9950                             colspan :  this.cm.getColumnCount()
9951                         }
9952                     ]
9953                 }
9954             ]
9955         };
9956         
9957         return body;
9958     },
9959     
9960     renderFooter : function()
9961     {
9962         var footer = {
9963             tag: 'tfoot',
9964             cn : [
9965                 {
9966                     tag: 'tr',
9967                     cn : [
9968                         {
9969                             tag : 'td',
9970                             colspan :  this.cm.getColumnCount()
9971                         }
9972                     ]
9973                 }
9974             ]
9975         };
9976         
9977         return footer;
9978     },
9979     
9980     
9981     
9982     onLoad : function()
9983     {
9984 //        Roo.log('ds onload');
9985         this.clear();
9986         
9987         var _this = this;
9988         var cm = this.cm;
9989         var ds = this.store;
9990         
9991         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9992             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9993             if (_this.store.sortInfo) {
9994                     
9995                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9996                     e.select('i', true).addClass(['fa-arrow-up']);
9997                 }
9998                 
9999                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10000                     e.select('i', true).addClass(['fa-arrow-down']);
10001                 }
10002             }
10003         });
10004         
10005         var tbody =  this.bodyEl;
10006               
10007         if(ds.getCount() > 0){
10008             ds.data.each(function(d,rowIndex){
10009                 var row =  this.renderRow(cm, ds, rowIndex);
10010                 
10011                 tbody.createChild(row);
10012                 
10013                 var _this = this;
10014                 
10015                 if(row.cellObjects.length){
10016                     Roo.each(row.cellObjects, function(r){
10017                         _this.renderCellObject(r);
10018                     })
10019                 }
10020                 
10021             }, this);
10022         } else if (this.empty_results.length) {
10023             this.el.mask(this.empty_results, 'no-spinner');
10024         }
10025         
10026         var tfoot = this.el.select('tfoot', true).first();
10027         
10028         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10029             
10030             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10031             
10032             var total = this.ds.getTotalCount();
10033             
10034             if(this.footer.pageSize < total){
10035                 this.mainFoot.show();
10036             }
10037         }
10038         
10039         Roo.each(this.el.select('tbody td', true).elements, function(e){
10040             e.on('mouseover', _this.onMouseover, _this);
10041         });
10042         
10043         Roo.each(this.el.select('tbody td', true).elements, function(e){
10044             e.on('mouseout', _this.onMouseout, _this);
10045         });
10046         this.fireEvent('rowsrendered', this);
10047         
10048         this.autoSize();
10049         
10050         this.initCSS(); /// resize cols
10051
10052         
10053     },
10054     
10055     
10056     onUpdate : function(ds,record)
10057     {
10058         this.refreshRow(record);
10059         this.autoSize();
10060     },
10061     
10062     onRemove : function(ds, record, index, isUpdate){
10063         if(isUpdate !== true){
10064             this.fireEvent("beforerowremoved", this, index, record);
10065         }
10066         var bt = this.bodyEl.dom;
10067         
10068         var rows = this.el.select('tbody > tr', true).elements;
10069         
10070         if(typeof(rows[index]) != 'undefined'){
10071             bt.removeChild(rows[index].dom);
10072         }
10073         
10074 //        if(bt.rows[index]){
10075 //            bt.removeChild(bt.rows[index]);
10076 //        }
10077         
10078         if(isUpdate !== true){
10079             //this.stripeRows(index);
10080             //this.syncRowHeights(index, index);
10081             //this.layout();
10082             this.fireEvent("rowremoved", this, index, record);
10083         }
10084     },
10085     
10086     onAdd : function(ds, records, rowIndex)
10087     {
10088         //Roo.log('on Add called');
10089         // - note this does not handle multiple adding very well..
10090         var bt = this.bodyEl.dom;
10091         for (var i =0 ; i < records.length;i++) {
10092             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10093             //Roo.log(records[i]);
10094             //Roo.log(this.store.getAt(rowIndex+i));
10095             this.insertRow(this.store, rowIndex + i, false);
10096             return;
10097         }
10098         
10099     },
10100     
10101     
10102     refreshRow : function(record){
10103         var ds = this.store, index;
10104         if(typeof record == 'number'){
10105             index = record;
10106             record = ds.getAt(index);
10107         }else{
10108             index = ds.indexOf(record);
10109             if (index < 0) {
10110                 return; // should not happen - but seems to 
10111             }
10112         }
10113         this.insertRow(ds, index, true);
10114         this.autoSize();
10115         this.onRemove(ds, record, index+1, true);
10116         this.autoSize();
10117         //this.syncRowHeights(index, index);
10118         //this.layout();
10119         this.fireEvent("rowupdated", this, index, record);
10120     },
10121     // private - called by RowSelection
10122     onRowSelect : function(rowIndex){
10123         var row = this.getRowDom(rowIndex);
10124         row.addClass(['bg-info','info']);
10125     },
10126     // private - called by RowSelection
10127     onRowDeselect : function(rowIndex)
10128     {
10129         if (rowIndex < 0) {
10130             return;
10131         }
10132         var row = this.getRowDom(rowIndex);
10133         row.removeClass(['bg-info','info']);
10134     },
10135       /**
10136      * Focuses the specified row.
10137      * @param {Number} row The row index
10138      */
10139     focusRow : function(row)
10140     {
10141         //Roo.log('GridView.focusRow');
10142         var x = this.bodyEl.dom.scrollLeft;
10143         this.focusCell(row, 0, false);
10144         this.bodyEl.dom.scrollLeft = x;
10145
10146     },
10147      /**
10148      * Focuses the specified cell.
10149      * @param {Number} row The row index
10150      * @param {Number} col The column index
10151      * @param {Boolean} hscroll false to disable horizontal scrolling
10152      */
10153     focusCell : function(row, col, hscroll)
10154     {
10155         //Roo.log('GridView.focusCell');
10156         var el = this.ensureVisible(row, col, hscroll);
10157         // not sure what focusEL achives = it's a <a> pos relative 
10158         //this.focusEl.alignTo(el, "tl-tl");
10159         //if(Roo.isGecko){
10160         //    this.focusEl.focus();
10161         //}else{
10162         //    this.focusEl.focus.defer(1, this.focusEl);
10163         //}
10164     },
10165     
10166      /**
10167      * Scrolls the specified cell into view
10168      * @param {Number} row The row index
10169      * @param {Number} col The column index
10170      * @param {Boolean} hscroll false to disable horizontal scrolling
10171      */
10172     ensureVisible : function(row, col, hscroll)
10173     {
10174         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10175         //return null; //disable for testing.
10176         if(typeof row != "number"){
10177             row = row.rowIndex;
10178         }
10179         if(row < 0 && row >= this.ds.getCount()){
10180             return  null;
10181         }
10182         col = (col !== undefined ? col : 0);
10183         var cm = this.cm;
10184         while(cm.isHidden(col)){
10185             col++;
10186         }
10187
10188         var el = this.getCellDom(row, col);
10189         if(!el){
10190             return null;
10191         }
10192         var c = this.bodyEl.dom;
10193
10194         var ctop = parseInt(el.offsetTop, 10);
10195         var cleft = parseInt(el.offsetLeft, 10);
10196         var cbot = ctop + el.offsetHeight;
10197         var cright = cleft + el.offsetWidth;
10198
10199         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10200         var ch = 0; //?? header is not withing the area?
10201         var stop = parseInt(c.scrollTop, 10);
10202         var sleft = parseInt(c.scrollLeft, 10);
10203         var sbot = stop + ch;
10204         var sright = sleft + c.clientWidth;
10205         /*
10206         Roo.log('GridView.ensureVisible:' +
10207                 ' ctop:' + ctop +
10208                 ' c.clientHeight:' + c.clientHeight +
10209                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10210                 ' stop:' + stop +
10211                 ' cbot:' + cbot +
10212                 ' sbot:' + sbot +
10213                 ' ch:' + ch  
10214                 );
10215         */
10216         if(ctop < stop){
10217             c.scrollTop = ctop;
10218             //Roo.log("set scrolltop to ctop DISABLE?");
10219         }else if(cbot > sbot){
10220             //Roo.log("set scrolltop to cbot-ch");
10221             c.scrollTop = cbot-ch;
10222         }
10223
10224         if(hscroll !== false){
10225             if(cleft < sleft){
10226                 c.scrollLeft = cleft;
10227             }else if(cright > sright){
10228                 c.scrollLeft = cright-c.clientWidth;
10229             }
10230         }
10231
10232         return el;
10233     },
10234     
10235     
10236     insertRow : function(dm, rowIndex, isUpdate){
10237         
10238         if(!isUpdate){
10239             this.fireEvent("beforerowsinserted", this, rowIndex);
10240         }
10241             //var s = this.getScrollState();
10242         var row = this.renderRow(this.cm, this.store, rowIndex);
10243         // insert before rowIndex..
10244         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10245         
10246         var _this = this;
10247                 
10248         if(row.cellObjects.length){
10249             Roo.each(row.cellObjects, function(r){
10250                 _this.renderCellObject(r);
10251             })
10252         }
10253             
10254         if(!isUpdate){
10255             this.fireEvent("rowsinserted", this, rowIndex);
10256             //this.syncRowHeights(firstRow, lastRow);
10257             //this.stripeRows(firstRow);
10258             //this.layout();
10259         }
10260         
10261     },
10262     
10263     
10264     getRowDom : function(rowIndex)
10265     {
10266         var rows = this.el.select('tbody > tr', true).elements;
10267         
10268         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10269         
10270     },
10271     getCellDom : function(rowIndex, colIndex)
10272     {
10273         var row = this.getRowDom(rowIndex);
10274         if (row === false) {
10275             return false;
10276         }
10277         var cols = row.select('td', true).elements;
10278         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10279         
10280     },
10281     
10282     // returns the object tree for a tr..
10283   
10284     
10285     renderRow : function(cm, ds, rowIndex) 
10286     {
10287         var d = ds.getAt(rowIndex);
10288         
10289         var row = {
10290             tag : 'tr',
10291             cls : 'x-row-' + rowIndex,
10292             cn : []
10293         };
10294             
10295         var cellObjects = [];
10296         
10297         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10298             var config = cm.config[i];
10299             
10300             var renderer = cm.getRenderer(i);
10301             var value = '';
10302             var id = false;
10303             
10304             if(typeof(renderer) !== 'undefined'){
10305                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10306             }
10307             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10308             // and are rendered into the cells after the row is rendered - using the id for the element.
10309             
10310             if(typeof(value) === 'object'){
10311                 id = Roo.id();
10312                 cellObjects.push({
10313                     container : id,
10314                     cfg : value 
10315                 })
10316             }
10317             
10318             var rowcfg = {
10319                 record: d,
10320                 rowIndex : rowIndex,
10321                 colIndex : i,
10322                 rowClass : ''
10323             };
10324
10325             this.fireEvent('rowclass', this, rowcfg);
10326             
10327             var td = {
10328                 tag: 'td',
10329                 // this might end up displaying HTML?
10330                 // this is too messy... - better to only do it on columsn you know are going to be too long
10331                 //tooltip : (typeof(value) === 'object') ? '' : value,
10332                 cls : rowcfg.rowClass + ' x-col-' + i,
10333                 style: '',
10334                 html: (typeof(value) === 'object') ? '' : value
10335             };
10336             
10337             if (id) {
10338                 td.id = id;
10339             }
10340             
10341             if(typeof(config.colspan) != 'undefined'){
10342                 td.colspan = config.colspan;
10343             }
10344             
10345             
10346             
10347             if(typeof(config.align) != 'undefined' && config.align.length){
10348                 td.style += ' text-align:' + config.align + ';';
10349             }
10350             if(typeof(config.valign) != 'undefined' && config.valign.length){
10351                 td.style += ' vertical-align:' + config.valign + ';';
10352             }
10353             /*
10354             if(typeof(config.width) != 'undefined'){
10355                 td.style += ' width:' +  config.width + 'px;';
10356             }
10357             */
10358             
10359             if(typeof(config.cursor) != 'undefined'){
10360                 td.style += ' cursor:' +  config.cursor + ';';
10361             }
10362             
10363             if(typeof(config.cls) != 'undefined'){
10364                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10365             }
10366             if (this.responsive) {
10367                 ['xs','sm','md','lg'].map(function(size){
10368                     
10369                     if(typeof(config[size]) == 'undefined'){
10370                         return;
10371                     }
10372                     
10373                     
10374                       
10375                     if (!config[size]) { // 0 = hidden
10376                         // BS 4 '0' is treated as hide that column and below.
10377                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10378                         return;
10379                     }
10380                     
10381                     td.cls += ' col-' + size + '-' + config[size] + (
10382                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10383                     );
10384                      
10385     
10386                 });
10387             }
10388             row.cn.push(td);
10389            
10390         }
10391         
10392         row.cellObjects = cellObjects;
10393         
10394         return row;
10395           
10396     },
10397     
10398     
10399     
10400     onBeforeLoad : function()
10401     {
10402         this.el.unmask(); // if needed.
10403     },
10404      /**
10405      * Remove all rows
10406      */
10407     clear : function()
10408     {
10409         this.el.select('tbody', true).first().dom.innerHTML = '';
10410     },
10411     /**
10412      * Show or hide a row.
10413      * @param {Number} rowIndex to show or hide
10414      * @param {Boolean} state hide
10415      */
10416     setRowVisibility : function(rowIndex, state)
10417     {
10418         var bt = this.bodyEl.dom;
10419         
10420         var rows = this.el.select('tbody > tr', true).elements;
10421         
10422         if(typeof(rows[rowIndex]) == 'undefined'){
10423             return;
10424         }
10425         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10426         
10427     },
10428     
10429     
10430     getSelectionModel : function(){
10431         if(!this.selModel){
10432             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10433         }
10434         return this.selModel;
10435     },
10436     /*
10437      * Render the Roo.bootstrap object from renderder
10438      */
10439     renderCellObject : function(r)
10440     {
10441         var _this = this;
10442         
10443         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10444         
10445         var t = r.cfg.render(r.container);
10446         
10447         if(r.cfg.cn){
10448             Roo.each(r.cfg.cn, function(c){
10449                 var child = {
10450                     container: t.getChildContainer(),
10451                     cfg: c
10452                 };
10453                 _this.renderCellObject(child);
10454             })
10455         }
10456     },
10457     /**
10458      * get the Row Index from a dom element.
10459      * @param {Roo.Element} row The row to look for
10460      * @returns {Number} the row
10461      */
10462     getRowIndex : function(row)
10463     {
10464         var rowIndex = -1;
10465         
10466         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10467             if(el != row){
10468                 return;
10469             }
10470             
10471             rowIndex = index;
10472         });
10473         
10474         return rowIndex;
10475     },
10476     /**
10477      * get the header TH element for columnIndex
10478      * @param {Number} columnIndex
10479      * @returns {Roo.Element}
10480      */
10481     getHeaderIndex: function(colIndex)
10482     {
10483         var cols = this.headEl.select('th', true).elements;
10484         return cols[colIndex]; 
10485     },
10486     /**
10487      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10488      * @param {domElement} cell to look for
10489      * @returns {Number} the column
10490      */
10491     getCellIndex : function(cell)
10492     {
10493         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10494         if(id){
10495             return parseInt(id[1], 10);
10496         }
10497         return 0;
10498     },
10499      /**
10500      * Returns the grid's underlying element = used by panel.Grid
10501      * @return {Element} The element
10502      */
10503     getGridEl : function(){
10504         return this.el;
10505     },
10506      /**
10507      * Forces a resize - used by panel.Grid
10508      * @return {Element} The element
10509      */
10510     autoSize : function()
10511     {
10512         if(this.disableAutoSize) {
10513             Roo.log("DISABLEAUTOSIZE");
10514             return;
10515         }
10516         //var ctr = Roo.get(this.container.dom.parentElement);
10517         var ctr = Roo.get(this.el.dom);
10518         
10519         var thd = this.getGridEl().select('thead',true).first();
10520         var tbd = this.getGridEl().select('tbody', true).first();
10521         var tfd = this.getGridEl().select('tfoot', true).first();
10522         
10523         var cw = ctr.getWidth();
10524         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10525         
10526         if (tbd) {
10527             
10528             tbd.setWidth(ctr.getWidth());
10529             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10530             // this needs fixing for various usage - currently only hydra job advers I think..
10531             //tdb.setHeight(
10532             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10533             //); 
10534             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10535             cw -= barsize;
10536         }
10537         cw = Math.max(cw, this.totalWidth);
10538         this.getGridEl().select('tbody tr',true).setWidth(cw);
10539         this.initCSS();
10540         
10541         // resize 'expandable coloumn?
10542         
10543         return; // we doe not have a view in this design..
10544         
10545     },
10546     onBodyScroll: function()
10547     {
10548         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10549         if(this.headEl){
10550             this.headEl.setStyle({
10551                 'position' : 'relative',
10552                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10553             });
10554         }
10555         
10556         if(this.lazyLoad){
10557             
10558             var scrollHeight = this.bodyEl.dom.scrollHeight;
10559             
10560             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10561             
10562             var height = this.bodyEl.getHeight();
10563             
10564             if(scrollHeight - height == scrollTop) {
10565                 
10566                 var total = this.ds.getTotalCount();
10567                 
10568                 if(this.footer.cursor + this.footer.pageSize < total){
10569                     
10570                     this.footer.ds.load({
10571                         params : {
10572                             start : this.footer.cursor + this.footer.pageSize,
10573                             limit : this.footer.pageSize
10574                         },
10575                         add : true
10576                     });
10577                 }
10578             }
10579             
10580         }
10581     },
10582     onColumnSplitterMoved : function(i, diff)
10583     {
10584         this.userResized = true;
10585         
10586         var cm = this.colModel;
10587         
10588         var w = this.getHeaderIndex(i).getWidth() + diff;
10589         
10590         
10591         cm.setColumnWidth(i, w, true);
10592         this.initCSS();
10593         //var cid = cm.getColumnId(i); << not used in this version?
10594        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10595         
10596         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10597         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10598         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10599 */
10600         //this.updateSplitters();
10601         //this.layout(); << ??
10602         this.fireEvent("columnresize", i, w);
10603     },
10604     onHeaderChange : function()
10605     {
10606         var header = this.renderHeader();
10607         var table = this.el.select('table', true).first();
10608         
10609         this.headEl.remove();
10610         this.headEl = table.createChild(header, this.bodyEl, false);
10611         
10612         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10613             e.on('click', this.sort, this);
10614         }, this);
10615         
10616         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10617             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10618         }
10619         
10620     },
10621     
10622     onHiddenChange : function(colModel, colIndex, hidden)
10623     {
10624         /*
10625         this.cm.setHidden()
10626         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10627         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10628         
10629         this.CSS.updateRule(thSelector, "display", "");
10630         this.CSS.updateRule(tdSelector, "display", "");
10631         
10632         if(hidden){
10633             this.CSS.updateRule(thSelector, "display", "none");
10634             this.CSS.updateRule(tdSelector, "display", "none");
10635         }
10636         */
10637         // onload calls initCSS()
10638         this.onHeaderChange();
10639         this.onLoad();
10640     },
10641     
10642     setColumnWidth: function(col_index, width)
10643     {
10644         // width = "md-2 xs-2..."
10645         if(!this.colModel.config[col_index]) {
10646             return;
10647         }
10648         
10649         var w = width.split(" ");
10650         
10651         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10652         
10653         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10654         
10655         
10656         for(var j = 0; j < w.length; j++) {
10657             
10658             if(!w[j]) {
10659                 continue;
10660             }
10661             
10662             var size_cls = w[j].split("-");
10663             
10664             if(!Number.isInteger(size_cls[1] * 1)) {
10665                 continue;
10666             }
10667             
10668             if(!this.colModel.config[col_index][size_cls[0]]) {
10669                 continue;
10670             }
10671             
10672             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10673                 continue;
10674             }
10675             
10676             h_row[0].classList.replace(
10677                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10678                 "col-"+size_cls[0]+"-"+size_cls[1]
10679             );
10680             
10681             for(var i = 0; i < rows.length; i++) {
10682                 
10683                 var size_cls = w[j].split("-");
10684                 
10685                 if(!Number.isInteger(size_cls[1] * 1)) {
10686                     continue;
10687                 }
10688                 
10689                 if(!this.colModel.config[col_index][size_cls[0]]) {
10690                     continue;
10691                 }
10692                 
10693                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10694                     continue;
10695                 }
10696                 
10697                 rows[i].classList.replace(
10698                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10699                     "col-"+size_cls[0]+"-"+size_cls[1]
10700                 );
10701             }
10702             
10703             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10704         }
10705     }
10706 });
10707
10708 // currently only used to find the split on drag.. 
10709 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10710
10711 /**
10712  * @depricated
10713 */
10714 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10715 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10716 /*
10717  * - LGPL
10718  *
10719  * table cell
10720  * 
10721  */
10722
10723 /**
10724  * @class Roo.bootstrap.TableCell
10725  * @extends Roo.bootstrap.Component
10726  * @children Roo.bootstrap.Component
10727  * @parent Roo.bootstrap.TableRow
10728  * Bootstrap TableCell class
10729  * 
10730  * @cfg {String} html cell contain text
10731  * @cfg {String} cls cell class
10732  * @cfg {String} tag cell tag (td|th) default td
10733  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10734  * @cfg {String} align Aligns the content in a cell
10735  * @cfg {String} axis Categorizes cells
10736  * @cfg {String} bgcolor Specifies the background color of a cell
10737  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10738  * @cfg {Number} colspan Specifies the number of columns a cell should span
10739  * @cfg {String} headers Specifies one or more header cells a cell is related to
10740  * @cfg {Number} height Sets the height of a cell
10741  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10742  * @cfg {Number} rowspan Sets the number of rows a cell should span
10743  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10744  * @cfg {String} valign Vertical aligns the content in a cell
10745  * @cfg {Number} width Specifies the width of a cell
10746  * 
10747  * @constructor
10748  * Create a new TableCell
10749  * @param {Object} config The config object
10750  */
10751
10752 Roo.bootstrap.TableCell = function(config){
10753     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10754 };
10755
10756 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10757     
10758     html: false,
10759     cls: false,
10760     tag: false,
10761     abbr: false,
10762     align: false,
10763     axis: false,
10764     bgcolor: false,
10765     charoff: false,
10766     colspan: false,
10767     headers: false,
10768     height: false,
10769     nowrap: false,
10770     rowspan: false,
10771     scope: false,
10772     valign: false,
10773     width: false,
10774     
10775     
10776     getAutoCreate : function(){
10777         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10778         
10779         cfg = {
10780             tag: 'td'
10781         };
10782         
10783         if(this.tag){
10784             cfg.tag = this.tag;
10785         }
10786         
10787         if (this.html) {
10788             cfg.html=this.html
10789         }
10790         if (this.cls) {
10791             cfg.cls=this.cls
10792         }
10793         if (this.abbr) {
10794             cfg.abbr=this.abbr
10795         }
10796         if (this.align) {
10797             cfg.align=this.align
10798         }
10799         if (this.axis) {
10800             cfg.axis=this.axis
10801         }
10802         if (this.bgcolor) {
10803             cfg.bgcolor=this.bgcolor
10804         }
10805         if (this.charoff) {
10806             cfg.charoff=this.charoff
10807         }
10808         if (this.colspan) {
10809             cfg.colspan=this.colspan
10810         }
10811         if (this.headers) {
10812             cfg.headers=this.headers
10813         }
10814         if (this.height) {
10815             cfg.height=this.height
10816         }
10817         if (this.nowrap) {
10818             cfg.nowrap=this.nowrap
10819         }
10820         if (this.rowspan) {
10821             cfg.rowspan=this.rowspan
10822         }
10823         if (this.scope) {
10824             cfg.scope=this.scope
10825         }
10826         if (this.valign) {
10827             cfg.valign=this.valign
10828         }
10829         if (this.width) {
10830             cfg.width=this.width
10831         }
10832         
10833         
10834         return cfg;
10835     }
10836    
10837 });
10838
10839  
10840
10841  /*
10842  * - LGPL
10843  *
10844  * table row
10845  * 
10846  */
10847
10848 /**
10849  * @class Roo.bootstrap.TableRow
10850  * @extends Roo.bootstrap.Component
10851  * @children Roo.bootstrap.TableCell
10852  * @parent Roo.bootstrap.TableBody
10853  * Bootstrap TableRow class
10854  * @cfg {String} cls row class
10855  * @cfg {String} align Aligns the content in a table row
10856  * @cfg {String} bgcolor Specifies a background color for a table row
10857  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10858  * @cfg {String} valign Vertical aligns the content in a table row
10859  * 
10860  * @constructor
10861  * Create a new TableRow
10862  * @param {Object} config The config object
10863  */
10864
10865 Roo.bootstrap.TableRow = function(config){
10866     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10867 };
10868
10869 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10870     
10871     cls: false,
10872     align: false,
10873     bgcolor: false,
10874     charoff: false,
10875     valign: false,
10876     
10877     getAutoCreate : function(){
10878         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10879         
10880         cfg = {
10881             tag: 'tr'
10882         };
10883             
10884         if(this.cls){
10885             cfg.cls = this.cls;
10886         }
10887         if(this.align){
10888             cfg.align = this.align;
10889         }
10890         if(this.bgcolor){
10891             cfg.bgcolor = this.bgcolor;
10892         }
10893         if(this.charoff){
10894             cfg.charoff = this.charoff;
10895         }
10896         if(this.valign){
10897             cfg.valign = this.valign;
10898         }
10899         
10900         return cfg;
10901     }
10902    
10903 });
10904
10905  
10906
10907  /*
10908  * - LGPL
10909  *
10910  * table body
10911  * 
10912  */
10913
10914 /**
10915  * @class Roo.bootstrap.TableBody
10916  * @extends Roo.bootstrap.Component
10917  * @children Roo.bootstrap.TableRow
10918  * @parent Roo.bootstrap.Table
10919  * Bootstrap TableBody class
10920  * @cfg {String} cls element class
10921  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10922  * @cfg {String} align Aligns the content inside the element
10923  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10924  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10925  * 
10926  * @constructor
10927  * Create a new TableBody
10928  * @param {Object} config The config object
10929  */
10930
10931 Roo.bootstrap.TableBody = function(config){
10932     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10933 };
10934
10935 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10936     
10937     cls: false,
10938     tag: false,
10939     align: false,
10940     charoff: false,
10941     valign: false,
10942     
10943     getAutoCreate : function(){
10944         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10945         
10946         cfg = {
10947             tag: 'tbody'
10948         };
10949             
10950         if (this.cls) {
10951             cfg.cls=this.cls
10952         }
10953         if(this.tag){
10954             cfg.tag = this.tag;
10955         }
10956         
10957         if(this.align){
10958             cfg.align = this.align;
10959         }
10960         if(this.charoff){
10961             cfg.charoff = this.charoff;
10962         }
10963         if(this.valign){
10964             cfg.valign = this.valign;
10965         }
10966         
10967         return cfg;
10968     }
10969     
10970     
10971 //    initEvents : function()
10972 //    {
10973 //        
10974 //        if(!this.store){
10975 //            return;
10976 //        }
10977 //        
10978 //        this.store = Roo.factory(this.store, Roo.data);
10979 //        this.store.on('load', this.onLoad, this);
10980 //        
10981 //        this.store.load();
10982 //        
10983 //    },
10984 //    
10985 //    onLoad: function () 
10986 //    {   
10987 //        this.fireEvent('load', this);
10988 //    }
10989 //    
10990 //   
10991 });
10992
10993  
10994
10995  /*
10996  * Based on:
10997  * Ext JS Library 1.1.1
10998  * Copyright(c) 2006-2007, Ext JS, LLC.
10999  *
11000  * Originally Released Under LGPL - original licence link has changed is not relivant.
11001  *
11002  * Fork - LGPL
11003  * <script type="text/javascript">
11004  */
11005
11006 // as we use this in bootstrap.
11007 Roo.namespace('Roo.form');
11008  /**
11009  * @class Roo.form.Action
11010  * Internal Class used to handle form actions
11011  * @constructor
11012  * @param {Roo.form.BasicForm} el The form element or its id
11013  * @param {Object} config Configuration options
11014  */
11015
11016  
11017  
11018 // define the action interface
11019 Roo.form.Action = function(form, options){
11020     this.form = form;
11021     this.options = options || {};
11022 };
11023 /**
11024  * Client Validation Failed
11025  * @const 
11026  */
11027 Roo.form.Action.CLIENT_INVALID = 'client';
11028 /**
11029  * Server Validation Failed
11030  * @const 
11031  */
11032 Roo.form.Action.SERVER_INVALID = 'server';
11033  /**
11034  * Connect to Server Failed
11035  * @const 
11036  */
11037 Roo.form.Action.CONNECT_FAILURE = 'connect';
11038 /**
11039  * Reading Data from Server Failed
11040  * @const 
11041  */
11042 Roo.form.Action.LOAD_FAILURE = 'load';
11043
11044 Roo.form.Action.prototype = {
11045     type : 'default',
11046     failureType : undefined,
11047     response : undefined,
11048     result : undefined,
11049
11050     // interface method
11051     run : function(options){
11052
11053     },
11054
11055     // interface method
11056     success : function(response){
11057
11058     },
11059
11060     // interface method
11061     handleResponse : function(response){
11062
11063     },
11064
11065     // default connection failure
11066     failure : function(response){
11067         
11068         this.response = response;
11069         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11070         this.form.afterAction(this, false);
11071     },
11072
11073     processResponse : function(response){
11074         this.response = response;
11075         if(!response.responseText){
11076             return true;
11077         }
11078         this.result = this.handleResponse(response);
11079         return this.result;
11080     },
11081
11082     // utility functions used internally
11083     getUrl : function(appendParams){
11084         var url = this.options.url || this.form.url || this.form.el.dom.action;
11085         if(appendParams){
11086             var p = this.getParams();
11087             if(p){
11088                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11089             }
11090         }
11091         return url;
11092     },
11093
11094     getMethod : function(){
11095         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11096     },
11097
11098     getParams : function(){
11099         var bp = this.form.baseParams;
11100         var p = this.options.params;
11101         if(p){
11102             if(typeof p == "object"){
11103                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11104             }else if(typeof p == 'string' && bp){
11105                 p += '&' + Roo.urlEncode(bp);
11106             }
11107         }else if(bp){
11108             p = Roo.urlEncode(bp);
11109         }
11110         return p;
11111     },
11112
11113     createCallback : function(){
11114         return {
11115             success: this.success,
11116             failure: this.failure,
11117             scope: this,
11118             timeout: (this.form.timeout*1000),
11119             upload: this.form.fileUpload ? this.success : undefined
11120         };
11121     }
11122 };
11123
11124 Roo.form.Action.Submit = function(form, options){
11125     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11126 };
11127
11128 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11129     type : 'submit',
11130
11131     haveProgress : false,
11132     uploadComplete : false,
11133     
11134     // uploadProgress indicator.
11135     uploadProgress : function()
11136     {
11137         if (!this.form.progressUrl) {
11138             return;
11139         }
11140         
11141         if (!this.haveProgress) {
11142             Roo.MessageBox.progress("Uploading", "Uploading");
11143         }
11144         if (this.uploadComplete) {
11145            Roo.MessageBox.hide();
11146            return;
11147         }
11148         
11149         this.haveProgress = true;
11150    
11151         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11152         
11153         var c = new Roo.data.Connection();
11154         c.request({
11155             url : this.form.progressUrl,
11156             params: {
11157                 id : uid
11158             },
11159             method: 'GET',
11160             success : function(req){
11161                //console.log(data);
11162                 var rdata = false;
11163                 var edata;
11164                 try  {
11165                    rdata = Roo.decode(req.responseText)
11166                 } catch (e) {
11167                     Roo.log("Invalid data from server..");
11168                     Roo.log(edata);
11169                     return;
11170                 }
11171                 if (!rdata || !rdata.success) {
11172                     Roo.log(rdata);
11173                     Roo.MessageBox.alert(Roo.encode(rdata));
11174                     return;
11175                 }
11176                 var data = rdata.data;
11177                 
11178                 if (this.uploadComplete) {
11179                    Roo.MessageBox.hide();
11180                    return;
11181                 }
11182                    
11183                 if (data){
11184                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11185                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11186                     );
11187                 }
11188                 this.uploadProgress.defer(2000,this);
11189             },
11190        
11191             failure: function(data) {
11192                 Roo.log('progress url failed ');
11193                 Roo.log(data);
11194             },
11195             scope : this
11196         });
11197            
11198     },
11199     
11200     
11201     run : function()
11202     {
11203         // run get Values on the form, so it syncs any secondary forms.
11204         this.form.getValues();
11205         
11206         var o = this.options;
11207         var method = this.getMethod();
11208         var isPost = method == 'POST';
11209         if(o.clientValidation === false || this.form.isValid()){
11210             
11211             if (this.form.progressUrl) {
11212                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11213                     (new Date() * 1) + '' + Math.random());
11214                     
11215             } 
11216             
11217             
11218             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11219                 form:this.form.el.dom,
11220                 url:this.getUrl(!isPost),
11221                 method: method,
11222                 params:isPost ? this.getParams() : null,
11223                 isUpload: this.form.fileUpload,
11224                 formData : this.form.formData
11225             }));
11226             
11227             this.uploadProgress();
11228
11229         }else if (o.clientValidation !== false){ // client validation failed
11230             this.failureType = Roo.form.Action.CLIENT_INVALID;
11231             this.form.afterAction(this, false);
11232         }
11233     },
11234
11235     success : function(response)
11236     {
11237         this.uploadComplete= true;
11238         if (this.haveProgress) {
11239             Roo.MessageBox.hide();
11240         }
11241         
11242         
11243         var result = this.processResponse(response);
11244         if(result === true || result.success){
11245             this.form.afterAction(this, true);
11246             return;
11247         }
11248         if(result.errors){
11249             this.form.markInvalid(result.errors);
11250             this.failureType = Roo.form.Action.SERVER_INVALID;
11251         }
11252         this.form.afterAction(this, false);
11253     },
11254     failure : function(response)
11255     {
11256         this.uploadComplete= true;
11257         if (this.haveProgress) {
11258             Roo.MessageBox.hide();
11259         }
11260         
11261         this.response = response;
11262         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11263         this.form.afterAction(this, false);
11264     },
11265     
11266     handleResponse : function(response){
11267         if(this.form.errorReader){
11268             var rs = this.form.errorReader.read(response);
11269             var errors = [];
11270             if(rs.records){
11271                 for(var i = 0, len = rs.records.length; i < len; i++) {
11272                     var r = rs.records[i];
11273                     errors[i] = r.data;
11274                 }
11275             }
11276             if(errors.length < 1){
11277                 errors = null;
11278             }
11279             return {
11280                 success : rs.success,
11281                 errors : errors
11282             };
11283         }
11284         var ret = false;
11285         try {
11286             ret = Roo.decode(response.responseText);
11287         } catch (e) {
11288             ret = {
11289                 success: false,
11290                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11291                 errors : []
11292             };
11293         }
11294         return ret;
11295         
11296     }
11297 });
11298
11299
11300 Roo.form.Action.Load = function(form, options){
11301     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11302     this.reader = this.form.reader;
11303 };
11304
11305 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11306     type : 'load',
11307
11308     run : function(){
11309         
11310         Roo.Ajax.request(Roo.apply(
11311                 this.createCallback(), {
11312                     method:this.getMethod(),
11313                     url:this.getUrl(false),
11314                     params:this.getParams()
11315         }));
11316     },
11317
11318     success : function(response){
11319         
11320         var result = this.processResponse(response);
11321         if(result === true || !result.success || !result.data){
11322             this.failureType = Roo.form.Action.LOAD_FAILURE;
11323             this.form.afterAction(this, false);
11324             return;
11325         }
11326         this.form.clearInvalid();
11327         this.form.setValues(result.data);
11328         this.form.afterAction(this, true);
11329     },
11330
11331     handleResponse : function(response){
11332         if(this.form.reader){
11333             var rs = this.form.reader.read(response);
11334             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11335             return {
11336                 success : rs.success,
11337                 data : data
11338             };
11339         }
11340         return Roo.decode(response.responseText);
11341     }
11342 });
11343
11344 Roo.form.Action.ACTION_TYPES = {
11345     'load' : Roo.form.Action.Load,
11346     'submit' : Roo.form.Action.Submit
11347 };/*
11348  * - LGPL
11349  *
11350  * form
11351  *
11352  */
11353
11354 /**
11355  * @class Roo.bootstrap.form.Form
11356  * @extends Roo.bootstrap.Component
11357  * @children Roo.bootstrap.Component
11358  * Bootstrap Form class
11359  * @cfg {String} method  GET | POST (default POST)
11360  * @cfg {String} labelAlign top | left (default top)
11361  * @cfg {String} align left  | right - for navbars
11362  * @cfg {Boolean} loadMask load mask when submit (default true)
11363
11364  *
11365  * @constructor
11366  * Create a new Form
11367  * @param {Object} config The config object
11368  */
11369
11370
11371 Roo.bootstrap.form.Form = function(config){
11372     
11373     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11374     
11375     Roo.bootstrap.form.Form.popover.apply();
11376     
11377     this.addEvents({
11378         /**
11379          * @event clientvalidation
11380          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11381          * @param {Form} this
11382          * @param {Boolean} valid true if the form has passed client-side validation
11383          */
11384         clientvalidation: true,
11385         /**
11386          * @event beforeaction
11387          * Fires before any action is performed. Return false to cancel the action.
11388          * @param {Form} this
11389          * @param {Action} action The action to be performed
11390          */
11391         beforeaction: true,
11392         /**
11393          * @event actionfailed
11394          * Fires when an action fails.
11395          * @param {Form} this
11396          * @param {Action} action The action that failed
11397          */
11398         actionfailed : true,
11399         /**
11400          * @event actioncomplete
11401          * Fires when an action is completed.
11402          * @param {Form} this
11403          * @param {Action} action The action that completed
11404          */
11405         actioncomplete : true
11406     });
11407 };
11408
11409 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11410
11411      /**
11412      * @cfg {String} method
11413      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11414      */
11415     method : 'POST',
11416     /**
11417      * @cfg {String} url
11418      * The URL to use for form actions if one isn't supplied in the action options.
11419      */
11420     /**
11421      * @cfg {Boolean} fileUpload
11422      * Set to true if this form is a file upload.
11423      */
11424
11425     /**
11426      * @cfg {Object} baseParams
11427      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11428      */
11429
11430     /**
11431      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11432      */
11433     timeout: 30,
11434     /**
11435      * @cfg {Sting} align (left|right) for navbar forms
11436      */
11437     align : 'left',
11438
11439     // private
11440     activeAction : null,
11441
11442     /**
11443      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11444      * element by passing it or its id or mask the form itself by passing in true.
11445      * @type Mixed
11446      */
11447     waitMsgTarget : false,
11448
11449     loadMask : true,
11450     
11451     /**
11452      * @cfg {Boolean} errorMask (true|false) default false
11453      */
11454     errorMask : false,
11455     
11456     /**
11457      * @cfg {Number} maskOffset Default 100
11458      */
11459     maskOffset : 100,
11460     
11461     /**
11462      * @cfg {Boolean} maskBody
11463      */
11464     maskBody : false,
11465
11466     getAutoCreate : function(){
11467
11468         var cfg = {
11469             tag: 'form',
11470             method : this.method || 'POST',
11471             id : this.id || Roo.id(),
11472             cls : ''
11473         };
11474         if (this.parent().xtype.match(/^Nav/)) {
11475             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11476
11477         }
11478
11479         if (this.labelAlign == 'left' ) {
11480             cfg.cls += ' form-horizontal';
11481         }
11482
11483
11484         return cfg;
11485     },
11486     initEvents : function()
11487     {
11488         this.el.on('submit', this.onSubmit, this);
11489         // this was added as random key presses on the form where triggering form submit.
11490         this.el.on('keypress', function(e) {
11491             if (e.getCharCode() != 13) {
11492                 return true;
11493             }
11494             // we might need to allow it for textareas.. and some other items.
11495             // check e.getTarget().
11496
11497             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11498                 return true;
11499             }
11500
11501             Roo.log("keypress blocked");
11502
11503             e.preventDefault();
11504             return false;
11505         });
11506         
11507     },
11508     // private
11509     onSubmit : function(e){
11510         e.stopEvent();
11511     },
11512
11513      /**
11514      * Returns true if client-side validation on the form is successful.
11515      * @return Boolean
11516      */
11517     isValid : function(){
11518         var items = this.getItems();
11519         var valid = true;
11520         var target = false;
11521         
11522         items.each(function(f){
11523             
11524             if(f.validate()){
11525                 return;
11526             }
11527             
11528             Roo.log('invalid field: ' + f.name);
11529             
11530             valid = false;
11531
11532             if(!target && f.el.isVisible(true)){
11533                 target = f;
11534             }
11535            
11536         });
11537         
11538         if(this.errorMask && !valid){
11539             Roo.bootstrap.form.Form.popover.mask(this, target);
11540         }
11541         
11542         return valid;
11543     },
11544     
11545     /**
11546      * Returns true if any fields in this form have changed since their original load.
11547      * @return Boolean
11548      */
11549     isDirty : function(){
11550         var dirty = false;
11551         var items = this.getItems();
11552         items.each(function(f){
11553            if(f.isDirty()){
11554                dirty = true;
11555                return false;
11556            }
11557            return true;
11558         });
11559         return dirty;
11560     },
11561      /**
11562      * Performs a predefined action (submit or load) or custom actions you define on this form.
11563      * @param {String} actionName The name of the action type
11564      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11565      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11566      * accept other config options):
11567      * <pre>
11568 Property          Type             Description
11569 ----------------  ---------------  ----------------------------------------------------------------------------------
11570 url               String           The url for the action (defaults to the form's url)
11571 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11572 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11573 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11574                                    validate the form on the client (defaults to false)
11575      * </pre>
11576      * @return {BasicForm} this
11577      */
11578     doAction : function(action, options){
11579         if(typeof action == 'string'){
11580             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11581         }
11582         if(this.fireEvent('beforeaction', this, action) !== false){
11583             this.beforeAction(action);
11584             action.run.defer(100, action);
11585         }
11586         return this;
11587     },
11588
11589     // private
11590     beforeAction : function(action){
11591         var o = action.options;
11592         
11593         if(this.loadMask){
11594             
11595             if(this.maskBody){
11596                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11597             } else {
11598                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11599             }
11600         }
11601         // not really supported yet.. ??
11602
11603         //if(this.waitMsgTarget === true){
11604         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11605         //}else if(this.waitMsgTarget){
11606         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11607         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11608         //}else {
11609         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11610        // }
11611
11612     },
11613
11614     // private
11615     afterAction : function(action, success){
11616         this.activeAction = null;
11617         var o = action.options;
11618
11619         if(this.loadMask){
11620             
11621             if(this.maskBody){
11622                 Roo.get(document.body).unmask();
11623             } else {
11624                 this.el.unmask();
11625             }
11626         }
11627         
11628         //if(this.waitMsgTarget === true){
11629 //            this.el.unmask();
11630         //}else if(this.waitMsgTarget){
11631         //    this.waitMsgTarget.unmask();
11632         //}else{
11633         //    Roo.MessageBox.updateProgress(1);
11634         //    Roo.MessageBox.hide();
11635        // }
11636         //
11637         if(success){
11638             if(o.reset){
11639                 this.reset();
11640             }
11641             Roo.callback(o.success, o.scope, [this, action]);
11642             this.fireEvent('actioncomplete', this, action);
11643
11644         }else{
11645
11646             // failure condition..
11647             // we have a scenario where updates need confirming.
11648             // eg. if a locking scenario exists..
11649             // we look for { errors : { needs_confirm : true }} in the response.
11650             if (
11651                 (typeof(action.result) != 'undefined')  &&
11652                 (typeof(action.result.errors) != 'undefined')  &&
11653                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11654            ){
11655                 var _t = this;
11656                 Roo.log("not supported yet");
11657                  /*
11658
11659                 Roo.MessageBox.confirm(
11660                     "Change requires confirmation",
11661                     action.result.errorMsg,
11662                     function(r) {
11663                         if (r != 'yes') {
11664                             return;
11665                         }
11666                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11667                     }
11668
11669                 );
11670                 */
11671
11672
11673                 return;
11674             }
11675
11676             Roo.callback(o.failure, o.scope, [this, action]);
11677             // show an error message if no failed handler is set..
11678             if (!this.hasListener('actionfailed')) {
11679                 Roo.log("need to add dialog support");
11680                 /*
11681                 Roo.MessageBox.alert("Error",
11682                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11683                         action.result.errorMsg :
11684                         "Saving Failed, please check your entries or try again"
11685                 );
11686                 */
11687             }
11688
11689             this.fireEvent('actionfailed', this, action);
11690         }
11691
11692     },
11693     /**
11694      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11695      * @param {String} id The value to search for
11696      * @return Field
11697      */
11698     findField : function(id){
11699         var items = this.getItems();
11700         var field = items.get(id);
11701         if(!field){
11702              items.each(function(f){
11703                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11704                     field = f;
11705                     return false;
11706                 }
11707                 return true;
11708             });
11709         }
11710         return field || null;
11711     },
11712      /**
11713      * Mark fields in this form invalid in bulk.
11714      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11715      * @return {BasicForm} this
11716      */
11717     markInvalid : function(errors){
11718         if(errors instanceof Array){
11719             for(var i = 0, len = errors.length; i < len; i++){
11720                 var fieldError = errors[i];
11721                 var f = this.findField(fieldError.id);
11722                 if(f){
11723                     f.markInvalid(fieldError.msg);
11724                 }
11725             }
11726         }else{
11727             var field, id;
11728             for(id in errors){
11729                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11730                     field.markInvalid(errors[id]);
11731                 }
11732             }
11733         }
11734         //Roo.each(this.childForms || [], function (f) {
11735         //    f.markInvalid(errors);
11736         //});
11737
11738         return this;
11739     },
11740
11741     /**
11742      * Set values for fields in this form in bulk.
11743      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11744      * @return {BasicForm} this
11745      */
11746     setValues : function(values){
11747         if(values instanceof Array){ // array of objects
11748             for(var i = 0, len = values.length; i < len; i++){
11749                 var v = values[i];
11750                 var f = this.findField(v.id);
11751                 if(f){
11752                     f.setValue(v.value);
11753                     if(this.trackResetOnLoad){
11754                         f.originalValue = f.getValue();
11755                     }
11756                 }
11757             }
11758         }else{ // object hash
11759             var field, id;
11760             for(id in values){
11761                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11762
11763                     if (field.setFromData &&
11764                         field.valueField &&
11765                         field.displayField &&
11766                         // combos' with local stores can
11767                         // be queried via setValue()
11768                         // to set their value..
11769                         (field.store && !field.store.isLocal)
11770                         ) {
11771                         // it's a combo
11772                         var sd = { };
11773                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11774                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11775                         field.setFromData(sd);
11776
11777                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11778                         
11779                         field.setFromData(values);
11780                         
11781                     } else {
11782                         field.setValue(values[id]);
11783                     }
11784
11785
11786                     if(this.trackResetOnLoad){
11787                         field.originalValue = field.getValue();
11788                     }
11789                 }
11790             }
11791         }
11792
11793         //Roo.each(this.childForms || [], function (f) {
11794         //    f.setValues(values);
11795         //});
11796
11797         return this;
11798     },
11799
11800     /**
11801      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11802      * they are returned as an array.
11803      * @param {Boolean} asString
11804      * @return {Object}
11805      */
11806     getValues : function(asString){
11807         //if (this.childForms) {
11808             // copy values from the child forms
11809         //    Roo.each(this.childForms, function (f) {
11810         //        this.setValues(f.getValues());
11811         //    }, this);
11812         //}
11813
11814
11815
11816         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11817         if(asString === true){
11818             return fs;
11819         }
11820         return Roo.urlDecode(fs);
11821     },
11822
11823     /**
11824      * Returns the fields in this form as an object with key/value pairs.
11825      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11826      * @return {Object}
11827      */
11828     getFieldValues : function(with_hidden)
11829     {
11830         var items = this.getItems();
11831         var ret = {};
11832         items.each(function(f){
11833             
11834             if (!f.getName()) {
11835                 return;
11836             }
11837             
11838             var v = f.getValue();
11839             
11840             if (f.inputType =='radio') {
11841                 if (typeof(ret[f.getName()]) == 'undefined') {
11842                     ret[f.getName()] = ''; // empty..
11843                 }
11844
11845                 if (!f.el.dom.checked) {
11846                     return;
11847
11848                 }
11849                 v = f.el.dom.value;
11850
11851             }
11852             
11853             if(f.xtype == 'MoneyField'){
11854                 ret[f.currencyName] = f.getCurrency();
11855             }
11856
11857             // not sure if this supported any more..
11858             if ((typeof(v) == 'object') && f.getRawValue) {
11859                 v = f.getRawValue() ; // dates..
11860             }
11861             // combo boxes where name != hiddenName...
11862             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11863                 ret[f.name] = f.getRawValue();
11864             }
11865             ret[f.getName()] = v;
11866         });
11867
11868         return ret;
11869     },
11870
11871     /**
11872      * Clears all invalid messages in this form.
11873      * @return {BasicForm} this
11874      */
11875     clearInvalid : function(){
11876         var items = this.getItems();
11877
11878         items.each(function(f){
11879            f.clearInvalid();
11880         });
11881
11882         return this;
11883     },
11884
11885     /**
11886      * Resets this form.
11887      * @return {BasicForm} this
11888      */
11889     reset : function(){
11890         var items = this.getItems();
11891         items.each(function(f){
11892             f.reset();
11893         });
11894
11895         Roo.each(this.childForms || [], function (f) {
11896             f.reset();
11897         });
11898
11899
11900         return this;
11901     },
11902     
11903     getItems : function()
11904     {
11905         var r=new Roo.util.MixedCollection(false, function(o){
11906             return o.id || (o.id = Roo.id());
11907         });
11908         var iter = function(el) {
11909             if (el.inputEl) {
11910                 r.add(el);
11911             }
11912             if (!el.items) {
11913                 return;
11914             }
11915             Roo.each(el.items,function(e) {
11916                 iter(e);
11917             });
11918         };
11919
11920         iter(this);
11921         return r;
11922     },
11923     
11924     hideFields : function(items)
11925     {
11926         Roo.each(items, function(i){
11927             
11928             var f = this.findField(i);
11929             
11930             if(!f){
11931                 return;
11932             }
11933             
11934             f.hide();
11935             
11936         }, this);
11937     },
11938     
11939     showFields : function(items)
11940     {
11941         Roo.each(items, function(i){
11942             
11943             var f = this.findField(i);
11944             
11945             if(!f){
11946                 return;
11947             }
11948             
11949             f.show();
11950             
11951         }, this);
11952     }
11953
11954 });
11955
11956 Roo.apply(Roo.bootstrap.form.Form, {
11957     
11958     popover : {
11959         
11960         padding : 5,
11961         
11962         isApplied : false,
11963         
11964         isMasked : false,
11965         
11966         form : false,
11967         
11968         target : false,
11969         
11970         toolTip : false,
11971         
11972         intervalID : false,
11973         
11974         maskEl : false,
11975         
11976         apply : function()
11977         {
11978             if(this.isApplied){
11979                 return;
11980             }
11981             
11982             this.maskEl = {
11983                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11984                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11985                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11986                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11987             };
11988             
11989             this.maskEl.top.enableDisplayMode("block");
11990             this.maskEl.left.enableDisplayMode("block");
11991             this.maskEl.bottom.enableDisplayMode("block");
11992             this.maskEl.right.enableDisplayMode("block");
11993             
11994             this.toolTip = new Roo.bootstrap.Tooltip({
11995                 cls : 'roo-form-error-popover',
11996                 alignment : {
11997                     'left' : ['r-l', [-2,0], 'right'],
11998                     'right' : ['l-r', [2,0], 'left'],
11999                     'bottom' : ['tl-bl', [0,2], 'top'],
12000                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12001                 }
12002             });
12003             
12004             this.toolTip.render(Roo.get(document.body));
12005
12006             this.toolTip.el.enableDisplayMode("block");
12007             
12008             Roo.get(document.body).on('click', function(){
12009                 this.unmask();
12010             }, this);
12011             
12012             Roo.get(document.body).on('touchstart', function(){
12013                 this.unmask();
12014             }, this);
12015             
12016             this.isApplied = true
12017         },
12018         
12019         mask : function(form, target)
12020         {
12021             this.form = form;
12022             
12023             this.target = target;
12024             
12025             if(!this.form.errorMask || !target.el){
12026                 return;
12027             }
12028             
12029             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12030             
12031             Roo.log(scrollable);
12032             
12033             var ot = this.target.el.calcOffsetsTo(scrollable);
12034             
12035             var scrollTo = ot[1] - this.form.maskOffset;
12036             
12037             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12038             
12039             scrollable.scrollTo('top', scrollTo);
12040             
12041             var box = this.target.el.getBox();
12042             Roo.log(box);
12043             var zIndex = Roo.bootstrap.Modal.zIndex++;
12044
12045             
12046             this.maskEl.top.setStyle('position', 'absolute');
12047             this.maskEl.top.setStyle('z-index', zIndex);
12048             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12049             this.maskEl.top.setLeft(0);
12050             this.maskEl.top.setTop(0);
12051             this.maskEl.top.show();
12052             
12053             this.maskEl.left.setStyle('position', 'absolute');
12054             this.maskEl.left.setStyle('z-index', zIndex);
12055             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12056             this.maskEl.left.setLeft(0);
12057             this.maskEl.left.setTop(box.y - this.padding);
12058             this.maskEl.left.show();
12059
12060             this.maskEl.bottom.setStyle('position', 'absolute');
12061             this.maskEl.bottom.setStyle('z-index', zIndex);
12062             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12063             this.maskEl.bottom.setLeft(0);
12064             this.maskEl.bottom.setTop(box.bottom + this.padding);
12065             this.maskEl.bottom.show();
12066
12067             this.maskEl.right.setStyle('position', 'absolute');
12068             this.maskEl.right.setStyle('z-index', zIndex);
12069             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12070             this.maskEl.right.setLeft(box.right + this.padding);
12071             this.maskEl.right.setTop(box.y - this.padding);
12072             this.maskEl.right.show();
12073
12074             this.toolTip.bindEl = this.target.el;
12075
12076             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12077
12078             var tip = this.target.blankText;
12079
12080             if(this.target.getValue() !== '' ) {
12081                 
12082                 if (this.target.invalidText.length) {
12083                     tip = this.target.invalidText;
12084                 } else if (this.target.regexText.length){
12085                     tip = this.target.regexText;
12086                 }
12087             }
12088
12089             this.toolTip.show(tip);
12090
12091             this.intervalID = window.setInterval(function() {
12092                 Roo.bootstrap.form.Form.popover.unmask();
12093             }, 10000);
12094
12095             window.onwheel = function(){ return false;};
12096             
12097             (function(){ this.isMasked = true; }).defer(500, this);
12098             
12099         },
12100         
12101         unmask : function()
12102         {
12103             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12104                 return;
12105             }
12106             
12107             this.maskEl.top.setStyle('position', 'absolute');
12108             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12109             this.maskEl.top.hide();
12110
12111             this.maskEl.left.setStyle('position', 'absolute');
12112             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12113             this.maskEl.left.hide();
12114
12115             this.maskEl.bottom.setStyle('position', 'absolute');
12116             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12117             this.maskEl.bottom.hide();
12118
12119             this.maskEl.right.setStyle('position', 'absolute');
12120             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12121             this.maskEl.right.hide();
12122             
12123             this.toolTip.hide();
12124             
12125             this.toolTip.el.hide();
12126             
12127             window.onwheel = function(){ return true;};
12128             
12129             if(this.intervalID){
12130                 window.clearInterval(this.intervalID);
12131                 this.intervalID = false;
12132             }
12133             
12134             this.isMasked = false;
12135             
12136         }
12137         
12138     }
12139     
12140 });
12141
12142 /*
12143  * Based on:
12144  * Ext JS Library 1.1.1
12145  * Copyright(c) 2006-2007, Ext JS, LLC.
12146  *
12147  * Originally Released Under LGPL - original licence link has changed is not relivant.
12148  *
12149  * Fork - LGPL
12150  * <script type="text/javascript">
12151  */
12152 /**
12153  * @class Roo.form.VTypes
12154  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12155  * @static
12156  */
12157 Roo.form.VTypes = function(){
12158     // closure these in so they are only created once.
12159     var alpha = /^[a-zA-Z_]+$/;
12160     var alphanum = /^[a-zA-Z0-9_]+$/;
12161     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12162     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12163
12164     // All these messages and functions are configurable
12165     return {
12166         /**
12167          * The function used to validate email addresses
12168          * @param {String} value The email address
12169          */
12170         'email' : function(v){
12171             return email.test(v);
12172         },
12173         /**
12174          * The error text to display when the email validation function returns false
12175          * @type String
12176          */
12177         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12178         /**
12179          * The keystroke filter mask to be applied on email input
12180          * @type RegExp
12181          */
12182         'emailMask' : /[a-z0-9_\.\-@]/i,
12183
12184         /**
12185          * The function used to validate URLs
12186          * @param {String} value The URL
12187          */
12188         'url' : function(v){
12189             return url.test(v);
12190         },
12191         /**
12192          * The error text to display when the url validation function returns false
12193          * @type String
12194          */
12195         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12196         
12197         /**
12198          * The function used to validate alpha values
12199          * @param {String} value The value
12200          */
12201         'alpha' : function(v){
12202             return alpha.test(v);
12203         },
12204         /**
12205          * The error text to display when the alpha validation function returns false
12206          * @type String
12207          */
12208         'alphaText' : 'This field should only contain letters and _',
12209         /**
12210          * The keystroke filter mask to be applied on alpha input
12211          * @type RegExp
12212          */
12213         'alphaMask' : /[a-z_]/i,
12214
12215         /**
12216          * The function used to validate alphanumeric values
12217          * @param {String} value The value
12218          */
12219         'alphanum' : function(v){
12220             return alphanum.test(v);
12221         },
12222         /**
12223          * The error text to display when the alphanumeric validation function returns false
12224          * @type String
12225          */
12226         'alphanumText' : 'This field should only contain letters, numbers and _',
12227         /**
12228          * The keystroke filter mask to be applied on alphanumeric input
12229          * @type RegExp
12230          */
12231         'alphanumMask' : /[a-z0-9_]/i
12232     };
12233 }();/*
12234  * - LGPL
12235  *
12236  * Input
12237  * 
12238  */
12239
12240 /**
12241  * @class Roo.bootstrap.form.Input
12242  * @extends Roo.bootstrap.Component
12243  * Bootstrap Input class
12244  * @cfg {Boolean} disabled is it disabled
12245  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12246  * @cfg {String} name name of the input
12247  * @cfg {string} fieldLabel - the label associated
12248  * @cfg {string} placeholder - placeholder to put in text.
12249  * @cfg {string} before - input group add on before
12250  * @cfg {string} after - input group add on after
12251  * @cfg {string} size - (lg|sm) or leave empty..
12252  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12253  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12254  * @cfg {Number} md colspan out of 12 for computer-sized screens
12255  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12256  * @cfg {string} value default value of the input
12257  * @cfg {Number} labelWidth set the width of label 
12258  * @cfg {Number} labellg set the width of label (1-12)
12259  * @cfg {Number} labelmd set the width of label (1-12)
12260  * @cfg {Number} labelsm set the width of label (1-12)
12261  * @cfg {Number} labelxs set the width of label (1-12)
12262  * @cfg {String} labelAlign (top|left)
12263  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12264  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12265  * @cfg {String} indicatorpos (left|right) default left
12266  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12267  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12268  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12269  * @cfg {Roo.bootstrap.Button} before Button to show before
12270  * @cfg {Roo.bootstrap.Button} afterButton to show before
12271  * @cfg {String} align (left|center|right) Default left
12272  * @cfg {Boolean} forceFeedback (true|false) Default false
12273  * 
12274  * @constructor
12275  * Create a new Input
12276  * @param {Object} config The config object
12277  */
12278
12279 Roo.bootstrap.form.Input = function(config){
12280     
12281     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12282     
12283     this.addEvents({
12284         /**
12285          * @event focus
12286          * Fires when this field receives input focus.
12287          * @param {Roo.form.Field} this
12288          */
12289         focus : true,
12290         /**
12291          * @event blur
12292          * Fires when this field loses input focus.
12293          * @param {Roo.form.Field} this
12294          */
12295         blur : true,
12296         /**
12297          * @event specialkey
12298          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12299          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12300          * @param {Roo.form.Field} this
12301          * @param {Roo.EventObject} e The event object
12302          */
12303         specialkey : true,
12304         /**
12305          * @event change
12306          * Fires just before the field blurs if the field value has changed.
12307          * @param {Roo.form.Field} this
12308          * @param {Mixed} newValue The new value
12309          * @param {Mixed} oldValue The original value
12310          */
12311         change : true,
12312         /**
12313          * @event invalid
12314          * Fires after the field has been marked as invalid.
12315          * @param {Roo.form.Field} this
12316          * @param {String} msg The validation message
12317          */
12318         invalid : true,
12319         /**
12320          * @event valid
12321          * Fires after the field has been validated with no errors.
12322          * @param {Roo.form.Field} this
12323          */
12324         valid : true,
12325          /**
12326          * @event keyup
12327          * Fires after the key up
12328          * @param {Roo.form.Field} this
12329          * @param {Roo.EventObject}  e The event Object
12330          */
12331         keyup : true,
12332         /**
12333          * @event paste
12334          * Fires after the user pastes into input
12335          * @param {Roo.form.Field} this
12336          * @param {Roo.EventObject}  e The event Object
12337          */
12338         paste : true
12339     });
12340 };
12341
12342 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12343      /**
12344      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12345       automatic validation (defaults to "keyup").
12346      */
12347     validationEvent : "keyup",
12348      /**
12349      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12350      */
12351     validateOnBlur : true,
12352     /**
12353      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12354      */
12355     validationDelay : 250,
12356      /**
12357      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12358      */
12359     focusClass : "x-form-focus",  // not needed???
12360     
12361        
12362     /**
12363      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12364      */
12365     invalidClass : "has-warning",
12366     
12367     /**
12368      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12369      */
12370     validClass : "has-success",
12371     
12372     /**
12373      * @cfg {Boolean} hasFeedback (true|false) default true
12374      */
12375     hasFeedback : true,
12376     
12377     /**
12378      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12379      */
12380     invalidFeedbackClass : "glyphicon-warning-sign",
12381     
12382     /**
12383      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12384      */
12385     validFeedbackClass : "glyphicon-ok",
12386     
12387     /**
12388      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12389      */
12390     selectOnFocus : false,
12391     
12392      /**
12393      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12394      */
12395     maskRe : null,
12396        /**
12397      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12398      */
12399     vtype : null,
12400     
12401       /**
12402      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12403      */
12404     disableKeyFilter : false,
12405     
12406        /**
12407      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12408      */
12409     disabled : false,
12410      /**
12411      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12412      */
12413     allowBlank : true,
12414     /**
12415      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12416      */
12417     blankText : "Please complete this mandatory field",
12418     
12419      /**
12420      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12421      */
12422     minLength : 0,
12423     /**
12424      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12425      */
12426     maxLength : Number.MAX_VALUE,
12427     /**
12428      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12429      */
12430     minLengthText : "The minimum length for this field is {0}",
12431     /**
12432      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12433      */
12434     maxLengthText : "The maximum length for this field is {0}",
12435   
12436     
12437     /**
12438      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12439      * If available, this function will be called only after the basic validators all return true, and will be passed the
12440      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12441      */
12442     validator : null,
12443     /**
12444      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12445      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12446      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12447      */
12448     regex : null,
12449     /**
12450      * @cfg {String} regexText -- Depricated - use Invalid Text
12451      */
12452     regexText : "",
12453     
12454     /**
12455      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12456      */
12457     invalidText : "",
12458     
12459     
12460     
12461     autocomplete: false,
12462     
12463     
12464     fieldLabel : '',
12465     inputType : 'text',
12466     
12467     name : false,
12468     placeholder: false,
12469     before : false,
12470     after : false,
12471     size : false,
12472     hasFocus : false,
12473     preventMark: false,
12474     isFormField : true,
12475     value : '',
12476     labelWidth : 2,
12477     labelAlign : false,
12478     readOnly : false,
12479     align : false,
12480     formatedValue : false,
12481     forceFeedback : false,
12482     
12483     indicatorpos : 'left',
12484     
12485     labellg : 0,
12486     labelmd : 0,
12487     labelsm : 0,
12488     labelxs : 0,
12489     
12490     capture : '',
12491     accept : '',
12492     
12493     parentLabelAlign : function()
12494     {
12495         var parent = this;
12496         while (parent.parent()) {
12497             parent = parent.parent();
12498             if (typeof(parent.labelAlign) !='undefined') {
12499                 return parent.labelAlign;
12500             }
12501         }
12502         return 'left';
12503         
12504     },
12505     
12506     getAutoCreate : function()
12507     {
12508         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12509         
12510         var id = Roo.id();
12511         
12512         var cfg = {};
12513         
12514         if(this.inputType != 'hidden'){
12515             cfg.cls = 'form-group' //input-group
12516         }
12517         
12518         var input =  {
12519             tag: 'input',
12520             id : id,
12521             type : this.inputType,
12522             value : this.value,
12523             cls : 'form-control',
12524             placeholder : this.placeholder || '',
12525             autocomplete : this.autocomplete || 'new-password'
12526         };
12527         if (this.inputType == 'file') {
12528             input.style = 'overflow:hidden'; // why not in CSS?
12529         }
12530         
12531         if(this.capture.length){
12532             input.capture = this.capture;
12533         }
12534         
12535         if(this.accept.length){
12536             input.accept = this.accept + "/*";
12537         }
12538         
12539         if(this.align){
12540             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12541         }
12542         
12543         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12544             input.maxLength = this.maxLength;
12545         }
12546         
12547         if (this.disabled) {
12548             input.disabled=true;
12549         }
12550         
12551         if (this.readOnly) {
12552             input.readonly=true;
12553         }
12554         
12555         if (this.name) {
12556             input.name = this.name;
12557         }
12558         
12559         if (this.size) {
12560             input.cls += ' input-' + this.size;
12561         }
12562         
12563         var settings=this;
12564         ['xs','sm','md','lg'].map(function(size){
12565             if (settings[size]) {
12566                 cfg.cls += ' col-' + size + '-' + settings[size];
12567             }
12568         });
12569         
12570         var inputblock = input;
12571         
12572         var feedback = {
12573             tag: 'span',
12574             cls: 'glyphicon form-control-feedback'
12575         };
12576             
12577         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12578             
12579             inputblock = {
12580                 cls : 'has-feedback',
12581                 cn :  [
12582                     input,
12583                     feedback
12584                 ] 
12585             };  
12586         }
12587         
12588         if (this.before || this.after) {
12589             
12590             inputblock = {
12591                 cls : 'input-group',
12592                 cn :  [] 
12593             };
12594             
12595             if (this.before && typeof(this.before) == 'string') {
12596                 
12597                 inputblock.cn.push({
12598                     tag :'span',
12599                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12600                     html : this.before
12601                 });
12602             }
12603             if (this.before && typeof(this.before) == 'object') {
12604                 this.before = Roo.factory(this.before);
12605                 
12606                 inputblock.cn.push({
12607                     tag :'span',
12608                     cls : 'roo-input-before input-group-prepend   input-group-' +
12609                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12610                 });
12611             }
12612             
12613             inputblock.cn.push(input);
12614             
12615             if (this.after && typeof(this.after) == 'string') {
12616                 inputblock.cn.push({
12617                     tag :'span',
12618                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12619                     html : this.after
12620                 });
12621             }
12622             if (this.after && typeof(this.after) == 'object') {
12623                 this.after = Roo.factory(this.after);
12624                 
12625                 inputblock.cn.push({
12626                     tag :'span',
12627                     cls : 'roo-input-after input-group-append  input-group-' +
12628                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12629                 });
12630             }
12631             
12632             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12633                 inputblock.cls += ' has-feedback';
12634                 inputblock.cn.push(feedback);
12635             }
12636         };
12637         var indicator = {
12638             tag : 'i',
12639             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12640             tooltip : 'This field is required'
12641         };
12642         if (this.allowBlank ) {
12643             indicator.style = this.allowBlank ? ' display:none' : '';
12644         }
12645         if (align ==='left' && this.fieldLabel.length) {
12646             
12647             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12648             
12649             cfg.cn = [
12650                 indicator,
12651                 {
12652                     tag: 'label',
12653                     'for' :  id,
12654                     cls : 'control-label col-form-label',
12655                     html : this.fieldLabel
12656
12657                 },
12658                 {
12659                     cls : "", 
12660                     cn: [
12661                         inputblock
12662                     ]
12663                 }
12664             ];
12665             
12666             var labelCfg = cfg.cn[1];
12667             var contentCfg = cfg.cn[2];
12668             
12669             if(this.indicatorpos == 'right'){
12670                 cfg.cn = [
12671                     {
12672                         tag: 'label',
12673                         'for' :  id,
12674                         cls : 'control-label col-form-label',
12675                         cn : [
12676                             {
12677                                 tag : 'span',
12678                                 html : this.fieldLabel
12679                             },
12680                             indicator
12681                         ]
12682                     },
12683                     {
12684                         cls : "",
12685                         cn: [
12686                             inputblock
12687                         ]
12688                     }
12689
12690                 ];
12691                 
12692                 labelCfg = cfg.cn[0];
12693                 contentCfg = cfg.cn[1];
12694             
12695             }
12696             
12697             if(this.labelWidth > 12){
12698                 labelCfg.style = "width: " + this.labelWidth + 'px';
12699             }
12700             
12701             if(this.labelWidth < 13 && this.labelmd == 0){
12702                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12703             }
12704             
12705             if(this.labellg > 0){
12706                 labelCfg.cls += ' col-lg-' + this.labellg;
12707                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12708             }
12709             
12710             if(this.labelmd > 0){
12711                 labelCfg.cls += ' col-md-' + this.labelmd;
12712                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12713             }
12714             
12715             if(this.labelsm > 0){
12716                 labelCfg.cls += ' col-sm-' + this.labelsm;
12717                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12718             }
12719             
12720             if(this.labelxs > 0){
12721                 labelCfg.cls += ' col-xs-' + this.labelxs;
12722                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12723             }
12724             
12725             
12726         } else if ( this.fieldLabel.length) {
12727                 
12728             
12729             
12730             cfg.cn = [
12731                 {
12732                     tag : 'i',
12733                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12734                     tooltip : 'This field is required',
12735                     style : this.allowBlank ? ' display:none' : '' 
12736                 },
12737                 {
12738                     tag: 'label',
12739                    //cls : 'input-group-addon',
12740                     html : this.fieldLabel
12741
12742                 },
12743
12744                inputblock
12745
12746            ];
12747            
12748            if(this.indicatorpos == 'right'){
12749        
12750                 cfg.cn = [
12751                     {
12752                         tag: 'label',
12753                        //cls : 'input-group-addon',
12754                         html : this.fieldLabel
12755
12756                     },
12757                     {
12758                         tag : 'i',
12759                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12760                         tooltip : 'This field is required',
12761                         style : this.allowBlank ? ' display:none' : '' 
12762                     },
12763
12764                    inputblock
12765
12766                ];
12767
12768             }
12769
12770         } else {
12771             
12772             cfg.cn = [
12773
12774                     inputblock
12775
12776             ];
12777                 
12778                 
12779         };
12780         
12781         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12782            cfg.cls += ' navbar-form';
12783         }
12784         
12785         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12786             // on BS4 we do this only if not form 
12787             cfg.cls += ' navbar-form';
12788             cfg.tag = 'li';
12789         }
12790         
12791         return cfg;
12792         
12793     },
12794     /**
12795      * return the real input element.
12796      */
12797     inputEl: function ()
12798     {
12799         return this.el.select('input.form-control',true).first();
12800     },
12801     
12802     tooltipEl : function()
12803     {
12804         return this.inputEl();
12805     },
12806     
12807     indicatorEl : function()
12808     {
12809         if (Roo.bootstrap.version == 4) {
12810             return false; // not enabled in v4 yet.
12811         }
12812         
12813         var indicator = this.el.select('i.roo-required-indicator',true).first();
12814         
12815         if(!indicator){
12816             return false;
12817         }
12818         
12819         return indicator;
12820         
12821     },
12822     
12823     setDisabled : function(v)
12824     {
12825         var i  = this.inputEl().dom;
12826         if (!v) {
12827             i.removeAttribute('disabled');
12828             return;
12829             
12830         }
12831         i.setAttribute('disabled','true');
12832     },
12833     initEvents : function()
12834     {
12835           
12836         this.inputEl().on("keydown" , this.fireKey,  this);
12837         this.inputEl().on("focus", this.onFocus,  this);
12838         this.inputEl().on("blur", this.onBlur,  this);
12839         
12840         this.inputEl().relayEvent('keyup', this);
12841         this.inputEl().relayEvent('paste', this);
12842         
12843         this.indicator = this.indicatorEl();
12844         
12845         if(this.indicator){
12846             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12847         }
12848  
12849         // reference to original value for reset
12850         this.originalValue = this.getValue();
12851         //Roo.form.TextField.superclass.initEvents.call(this);
12852         if(this.validationEvent == 'keyup'){
12853             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12854             this.inputEl().on('keyup', this.filterValidation, this);
12855         }
12856         else if(this.validationEvent !== false){
12857             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12858         }
12859         
12860         if(this.selectOnFocus){
12861             this.on("focus", this.preFocus, this);
12862             
12863         }
12864         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12865             this.inputEl().on("keypress", this.filterKeys, this);
12866         } else {
12867             this.inputEl().relayEvent('keypress', this);
12868         }
12869        /* if(this.grow){
12870             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12871             this.el.on("click", this.autoSize,  this);
12872         }
12873         */
12874         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12875             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12876         }
12877         
12878         if (typeof(this.before) == 'object') {
12879             this.before.render(this.el.select('.roo-input-before',true).first());
12880         }
12881         if (typeof(this.after) == 'object') {
12882             this.after.render(this.el.select('.roo-input-after',true).first());
12883         }
12884         
12885         this.inputEl().on('change', this.onChange, this);
12886         
12887     },
12888     filterValidation : function(e){
12889         if(!e.isNavKeyPress()){
12890             this.validationTask.delay(this.validationDelay);
12891         }
12892     },
12893      /**
12894      * Validates the field value
12895      * @return {Boolean} True if the value is valid, else false
12896      */
12897     validate : function(){
12898         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12899         if(this.disabled || this.validateValue(this.getRawValue())){
12900             this.markValid();
12901             return true;
12902         }
12903         
12904         this.markInvalid();
12905         return false;
12906     },
12907     
12908     
12909     /**
12910      * Validates a value according to the field's validation rules and marks the field as invalid
12911      * if the validation fails
12912      * @param {Mixed} value The value to validate
12913      * @return {Boolean} True if the value is valid, else false
12914      */
12915     validateValue : function(value)
12916     {
12917         if(this.getVisibilityEl().hasClass('hidden')){
12918             return true;
12919         }
12920         
12921         if(value.length < 1)  { // if it's blank
12922             if(this.allowBlank){
12923                 return true;
12924             }
12925             return false;
12926         }
12927         
12928         if(value.length < this.minLength){
12929             return false;
12930         }
12931         if(value.length > this.maxLength){
12932             return false;
12933         }
12934         if(this.vtype){
12935             var vt = Roo.form.VTypes;
12936             if(!vt[this.vtype](value, this)){
12937                 return false;
12938             }
12939         }
12940         if(typeof this.validator == "function"){
12941             var msg = this.validator(value);
12942             if(msg !== true){
12943                 return false;
12944             }
12945             if (typeof(msg) == 'string') {
12946                 this.invalidText = msg;
12947             }
12948         }
12949         
12950         if(this.regex && !this.regex.test(value)){
12951             return false;
12952         }
12953         
12954         return true;
12955     },
12956     
12957      // private
12958     fireKey : function(e){
12959         //Roo.log('field ' + e.getKey());
12960         if(e.isNavKeyPress()){
12961             this.fireEvent("specialkey", this, e);
12962         }
12963     },
12964     focus : function (selectText){
12965         if(this.rendered){
12966             this.inputEl().focus();
12967             if(selectText === true){
12968                 this.inputEl().dom.select();
12969             }
12970         }
12971         return this;
12972     } ,
12973     
12974     onFocus : function(){
12975         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12976            // this.el.addClass(this.focusClass);
12977         }
12978         if(!this.hasFocus){
12979             this.hasFocus = true;
12980             this.startValue = this.getValue();
12981             this.fireEvent("focus", this);
12982         }
12983     },
12984     
12985     beforeBlur : Roo.emptyFn,
12986
12987     
12988     // private
12989     onBlur : function(){
12990         this.beforeBlur();
12991         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12992             //this.el.removeClass(this.focusClass);
12993         }
12994         this.hasFocus = false;
12995         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12996             this.validate();
12997         }
12998         var v = this.getValue();
12999         if(String(v) !== String(this.startValue)){
13000             this.fireEvent('change', this, v, this.startValue);
13001         }
13002         this.fireEvent("blur", this);
13003     },
13004     
13005     onChange : function(e)
13006     {
13007         var v = this.getValue();
13008         if(String(v) !== String(this.startValue)){
13009             this.fireEvent('change', this, v, this.startValue);
13010         }
13011         
13012     },
13013     
13014     /**
13015      * Resets the current field value to the originally loaded value and clears any validation messages
13016      */
13017     reset : function(){
13018         this.setValue(this.originalValue);
13019         this.validate();
13020     },
13021      /**
13022      * Returns the name of the field
13023      * @return {Mixed} name The name field
13024      */
13025     getName: function(){
13026         return this.name;
13027     },
13028      /**
13029      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13030      * @return {Mixed} value The field value
13031      */
13032     getValue : function(){
13033         
13034         var v = this.inputEl().getValue();
13035         
13036         return v;
13037     },
13038     /**
13039      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13040      * @return {Mixed} value The field value
13041      */
13042     getRawValue : function(){
13043         var v = this.inputEl().getValue();
13044         
13045         return v;
13046     },
13047     
13048     /**
13049      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13050      * @param {Mixed} value The value to set
13051      */
13052     setRawValue : function(v){
13053         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13054     },
13055     
13056     selectText : function(start, end){
13057         var v = this.getRawValue();
13058         if(v.length > 0){
13059             start = start === undefined ? 0 : start;
13060             end = end === undefined ? v.length : end;
13061             var d = this.inputEl().dom;
13062             if(d.setSelectionRange){
13063                 d.setSelectionRange(start, end);
13064             }else if(d.createTextRange){
13065                 var range = d.createTextRange();
13066                 range.moveStart("character", start);
13067                 range.moveEnd("character", v.length-end);
13068                 range.select();
13069             }
13070         }
13071     },
13072     
13073     /**
13074      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13075      * @param {Mixed} value The value to set
13076      */
13077     setValue : function(v){
13078         this.value = v;
13079         if(this.rendered){
13080             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13081             this.validate();
13082         }
13083     },
13084     
13085     /*
13086     processValue : function(value){
13087         if(this.stripCharsRe){
13088             var newValue = value.replace(this.stripCharsRe, '');
13089             if(newValue !== value){
13090                 this.setRawValue(newValue);
13091                 return newValue;
13092             }
13093         }
13094         return value;
13095     },
13096   */
13097     preFocus : function(){
13098         
13099         if(this.selectOnFocus){
13100             this.inputEl().dom.select();
13101         }
13102     },
13103     filterKeys : function(e){
13104         var k = e.getKey();
13105         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13106             return;
13107         }
13108         var c = e.getCharCode(), cc = String.fromCharCode(c);
13109         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13110             return;
13111         }
13112         if(!this.maskRe.test(cc)){
13113             e.stopEvent();
13114         }
13115     },
13116      /**
13117      * Clear any invalid styles/messages for this field
13118      */
13119     clearInvalid : function(){
13120         
13121         if(!this.el || this.preventMark){ // not rendered
13122             return;
13123         }
13124         
13125         
13126         this.el.removeClass([this.invalidClass, 'is-invalid']);
13127         
13128         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13129             
13130             var feedback = this.el.select('.form-control-feedback', true).first();
13131             
13132             if(feedback){
13133                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13134             }
13135             
13136         }
13137         
13138         if(this.indicator){
13139             this.indicator.removeClass('visible');
13140             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13141         }
13142         
13143         this.fireEvent('valid', this);
13144     },
13145     
13146      /**
13147      * Mark this field as valid
13148      */
13149     markValid : function()
13150     {
13151         if(!this.el  || this.preventMark){ // not rendered...
13152             return;
13153         }
13154         
13155         this.el.removeClass([this.invalidClass, this.validClass]);
13156         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13157
13158         var feedback = this.el.select('.form-control-feedback', true).first();
13159             
13160         if(feedback){
13161             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13162         }
13163         
13164         if(this.indicator){
13165             this.indicator.removeClass('visible');
13166             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13167         }
13168         
13169         if(this.disabled){
13170             return;
13171         }
13172         
13173            
13174         if(this.allowBlank && !this.getRawValue().length){
13175             return;
13176         }
13177         if (Roo.bootstrap.version == 3) {
13178             this.el.addClass(this.validClass);
13179         } else {
13180             this.inputEl().addClass('is-valid');
13181         }
13182
13183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13184             
13185             var feedback = this.el.select('.form-control-feedback', true).first();
13186             
13187             if(feedback){
13188                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13189                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13190             }
13191             
13192         }
13193         
13194         this.fireEvent('valid', this);
13195     },
13196     
13197      /**
13198      * Mark this field as invalid
13199      * @param {String} msg The validation message
13200      */
13201     markInvalid : function(msg)
13202     {
13203         if(!this.el  || this.preventMark){ // not rendered
13204             return;
13205         }
13206         
13207         this.el.removeClass([this.invalidClass, this.validClass]);
13208         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13209         
13210         var feedback = this.el.select('.form-control-feedback', true).first();
13211             
13212         if(feedback){
13213             this.el.select('.form-control-feedback', true).first().removeClass(
13214                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13215         }
13216
13217         if(this.disabled){
13218             return;
13219         }
13220         
13221         if(this.allowBlank && !this.getRawValue().length){
13222             return;
13223         }
13224         
13225         if(this.indicator){
13226             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13227             this.indicator.addClass('visible');
13228         }
13229         if (Roo.bootstrap.version == 3) {
13230             this.el.addClass(this.invalidClass);
13231         } else {
13232             this.inputEl().addClass('is-invalid');
13233         }
13234         
13235         
13236         
13237         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13238             
13239             var feedback = this.el.select('.form-control-feedback', true).first();
13240             
13241             if(feedback){
13242                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13243                 
13244                 if(this.getValue().length || this.forceFeedback){
13245                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13246                 }
13247                 
13248             }
13249             
13250         }
13251         
13252         this.fireEvent('invalid', this, msg);
13253     },
13254     // private
13255     SafariOnKeyDown : function(event)
13256     {
13257         // this is a workaround for a password hang bug on chrome/ webkit.
13258         if (this.inputEl().dom.type != 'password') {
13259             return;
13260         }
13261         
13262         var isSelectAll = false;
13263         
13264         if(this.inputEl().dom.selectionEnd > 0){
13265             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13266         }
13267         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13268             event.preventDefault();
13269             this.setValue('');
13270             return;
13271         }
13272         
13273         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13274             
13275             event.preventDefault();
13276             // this is very hacky as keydown always get's upper case.
13277             //
13278             var cc = String.fromCharCode(event.getCharCode());
13279             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13280             
13281         }
13282     },
13283     adjustWidth : function(tag, w){
13284         tag = tag.toLowerCase();
13285         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13286             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13287                 if(tag == 'input'){
13288                     return w + 2;
13289                 }
13290                 if(tag == 'textarea'){
13291                     return w-2;
13292                 }
13293             }else if(Roo.isOpera){
13294                 if(tag == 'input'){
13295                     return w + 2;
13296                 }
13297                 if(tag == 'textarea'){
13298                     return w-2;
13299                 }
13300             }
13301         }
13302         return w;
13303     },
13304     
13305     setFieldLabel : function(v)
13306     {
13307         if(!this.rendered){
13308             return;
13309         }
13310         
13311         if(this.indicatorEl()){
13312             var ar = this.el.select('label > span',true);
13313             
13314             if (ar.elements.length) {
13315                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13316                 this.fieldLabel = v;
13317                 return;
13318             }
13319             
13320             var br = this.el.select('label',true);
13321             
13322             if(br.elements.length) {
13323                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13324                 this.fieldLabel = v;
13325                 return;
13326             }
13327             
13328             Roo.log('Cannot Found any of label > span || label in input');
13329             return;
13330         }
13331         
13332         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13333         this.fieldLabel = v;
13334         
13335         
13336     }
13337 });
13338
13339  
13340 /*
13341  * - LGPL
13342  *
13343  * Input
13344  * 
13345  */
13346
13347 /**
13348  * @class Roo.bootstrap.form.TextArea
13349  * @extends Roo.bootstrap.form.Input
13350  * Bootstrap TextArea class
13351  * @cfg {Number} cols Specifies the visible width of a text area
13352  * @cfg {Number} rows Specifies the visible number of lines in a text area
13353  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13354  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13355  * @cfg {string} html text
13356  * 
13357  * @constructor
13358  * Create a new TextArea
13359  * @param {Object} config The config object
13360  */
13361
13362 Roo.bootstrap.form.TextArea = function(config){
13363     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13364    
13365 };
13366
13367 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13368      
13369     cols : false,
13370     rows : 5,
13371     readOnly : false,
13372     warp : 'soft',
13373     resize : false,
13374     value: false,
13375     html: false,
13376     
13377     getAutoCreate : function(){
13378         
13379         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13380         
13381         var id = Roo.id();
13382         
13383         var cfg = {};
13384         
13385         if(this.inputType != 'hidden'){
13386             cfg.cls = 'form-group' //input-group
13387         }
13388         
13389         var input =  {
13390             tag: 'textarea',
13391             id : id,
13392             warp : this.warp,
13393             rows : this.rows,
13394             value : this.value || '',
13395             html: this.html || '',
13396             cls : 'form-control',
13397             placeholder : this.placeholder || '' 
13398             
13399         };
13400         
13401         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13402             input.maxLength = this.maxLength;
13403         }
13404         
13405         if(this.resize){
13406             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13407         }
13408         
13409         if(this.cols){
13410             input.cols = this.cols;
13411         }
13412         
13413         if (this.readOnly) {
13414             input.readonly = true;
13415         }
13416         
13417         if (this.name) {
13418             input.name = this.name;
13419         }
13420         
13421         if (this.size) {
13422             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13423         }
13424         
13425         var settings=this;
13426         ['xs','sm','md','lg'].map(function(size){
13427             if (settings[size]) {
13428                 cfg.cls += ' col-' + size + '-' + settings[size];
13429             }
13430         });
13431         
13432         var inputblock = input;
13433         
13434         if(this.hasFeedback && !this.allowBlank){
13435             
13436             var feedback = {
13437                 tag: 'span',
13438                 cls: 'glyphicon form-control-feedback'
13439             };
13440
13441             inputblock = {
13442                 cls : 'has-feedback',
13443                 cn :  [
13444                     input,
13445                     feedback
13446                 ] 
13447             };  
13448         }
13449         
13450         
13451         if (this.before || this.after) {
13452             
13453             inputblock = {
13454                 cls : 'input-group',
13455                 cn :  [] 
13456             };
13457             if (this.before) {
13458                 inputblock.cn.push({
13459                     tag :'span',
13460                     cls : 'input-group-addon',
13461                     html : this.before
13462                 });
13463             }
13464             
13465             inputblock.cn.push(input);
13466             
13467             if(this.hasFeedback && !this.allowBlank){
13468                 inputblock.cls += ' has-feedback';
13469                 inputblock.cn.push(feedback);
13470             }
13471             
13472             if (this.after) {
13473                 inputblock.cn.push({
13474                     tag :'span',
13475                     cls : 'input-group-addon',
13476                     html : this.after
13477                 });
13478             }
13479             
13480         }
13481         
13482         if (align ==='left' && this.fieldLabel.length) {
13483             cfg.cn = [
13484                 {
13485                     tag: 'label',
13486                     'for' :  id,
13487                     cls : 'control-label',
13488                     html : this.fieldLabel
13489                 },
13490                 {
13491                     cls : "",
13492                     cn: [
13493                         inputblock
13494                     ]
13495                 }
13496
13497             ];
13498             
13499             if(this.labelWidth > 12){
13500                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13501             }
13502
13503             if(this.labelWidth < 13 && this.labelmd == 0){
13504                 this.labelmd = this.labelWidth;
13505             }
13506
13507             if(this.labellg > 0){
13508                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13509                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13510             }
13511
13512             if(this.labelmd > 0){
13513                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13514                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13515             }
13516
13517             if(this.labelsm > 0){
13518                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13519                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13520             }
13521
13522             if(this.labelxs > 0){
13523                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13524                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13525             }
13526             
13527         } else if ( this.fieldLabel.length) {
13528             cfg.cn = [
13529
13530                {
13531                    tag: 'label',
13532                    //cls : 'input-group-addon',
13533                    html : this.fieldLabel
13534
13535                },
13536
13537                inputblock
13538
13539            ];
13540
13541         } else {
13542
13543             cfg.cn = [
13544
13545                 inputblock
13546
13547             ];
13548                 
13549         }
13550         
13551         if (this.disabled) {
13552             input.disabled=true;
13553         }
13554         
13555         return cfg;
13556         
13557     },
13558     /**
13559      * return the real textarea element.
13560      */
13561     inputEl: function ()
13562     {
13563         return this.el.select('textarea.form-control',true).first();
13564     },
13565     
13566     /**
13567      * Clear any invalid styles/messages for this field
13568      */
13569     clearInvalid : function()
13570     {
13571         
13572         if(!this.el || this.preventMark){ // not rendered
13573             return;
13574         }
13575         
13576         var label = this.el.select('label', true).first();
13577         var icon = this.el.select('i.fa-star', true).first();
13578         
13579         if(label && icon){
13580             icon.remove();
13581         }
13582         this.el.removeClass( this.validClass);
13583         this.inputEl().removeClass('is-invalid');
13584          
13585         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13586             
13587             var feedback = this.el.select('.form-control-feedback', true).first();
13588             
13589             if(feedback){
13590                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13591             }
13592             
13593         }
13594         
13595         this.fireEvent('valid', this);
13596     },
13597     
13598      /**
13599      * Mark this field as valid
13600      */
13601     markValid : function()
13602     {
13603         if(!this.el  || this.preventMark){ // not rendered
13604             return;
13605         }
13606         
13607         this.el.removeClass([this.invalidClass, this.validClass]);
13608         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13609         
13610         var feedback = this.el.select('.form-control-feedback', true).first();
13611             
13612         if(feedback){
13613             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13614         }
13615
13616         if(this.disabled || this.allowBlank){
13617             return;
13618         }
13619         
13620         var label = this.el.select('label', true).first();
13621         var icon = this.el.select('i.fa-star', true).first();
13622         
13623         if(label && icon){
13624             icon.remove();
13625         }
13626         if (Roo.bootstrap.version == 3) {
13627             this.el.addClass(this.validClass);
13628         } else {
13629             this.inputEl().addClass('is-valid');
13630         }
13631         
13632         
13633         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13634             
13635             var feedback = this.el.select('.form-control-feedback', true).first();
13636             
13637             if(feedback){
13638                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13639                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13640             }
13641             
13642         }
13643         
13644         this.fireEvent('valid', this);
13645     },
13646     
13647      /**
13648      * Mark this field as invalid
13649      * @param {String} msg The validation message
13650      */
13651     markInvalid : function(msg)
13652     {
13653         if(!this.el  || this.preventMark){ // not rendered
13654             return;
13655         }
13656         
13657         this.el.removeClass([this.invalidClass, this.validClass]);
13658         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13659         
13660         var feedback = this.el.select('.form-control-feedback', true).first();
13661             
13662         if(feedback){
13663             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13664         }
13665
13666         if(this.disabled || this.allowBlank){
13667             return;
13668         }
13669         
13670         var label = this.el.select('label', true).first();
13671         var icon = this.el.select('i.fa-star', true).first();
13672         
13673         if(!this.getValue().length && label && !icon){
13674             this.el.createChild({
13675                 tag : 'i',
13676                 cls : 'text-danger fa fa-lg fa-star',
13677                 tooltip : 'This field is required',
13678                 style : 'margin-right:5px;'
13679             }, label, true);
13680         }
13681         
13682         if (Roo.bootstrap.version == 3) {
13683             this.el.addClass(this.invalidClass);
13684         } else {
13685             this.inputEl().addClass('is-invalid');
13686         }
13687         
13688         // fixme ... this may be depricated need to test..
13689         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13690             
13691             var feedback = this.el.select('.form-control-feedback', true).first();
13692             
13693             if(feedback){
13694                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13695                 
13696                 if(this.getValue().length || this.forceFeedback){
13697                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13698                 }
13699                 
13700             }
13701             
13702         }
13703         
13704         this.fireEvent('invalid', this, msg);
13705     }
13706 });
13707
13708  
13709 /*
13710  * - LGPL
13711  *
13712  * trigger field - base class for combo..
13713  * 
13714  */
13715  
13716 /**
13717  * @class Roo.bootstrap.form.TriggerField
13718  * @extends Roo.bootstrap.form.Input
13719  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13720  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13721  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13722  * for which you can provide a custom implementation.  For example:
13723  * <pre><code>
13724 var trigger = new Roo.bootstrap.form.TriggerField();
13725 trigger.onTriggerClick = myTriggerFn;
13726 trigger.applyTo('my-field');
13727 </code></pre>
13728  *
13729  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13730  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13731  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13732  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13733  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13734
13735  * @constructor
13736  * Create a new TriggerField.
13737  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13738  * to the base TextField)
13739  */
13740 Roo.bootstrap.form.TriggerField = function(config){
13741     this.mimicing = false;
13742     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13743 };
13744
13745 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13746     /**
13747      * @cfg {String} triggerClass A CSS class to apply to the trigger
13748      */
13749      /**
13750      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13751      */
13752     hideTrigger:false,
13753
13754     /**
13755      * @cfg {Boolean} removable (true|false) special filter default false
13756      */
13757     removable : false,
13758     
13759     /** @cfg {Boolean} grow @hide */
13760     /** @cfg {Number} growMin @hide */
13761     /** @cfg {Number} growMax @hide */
13762
13763     /**
13764      * @hide 
13765      * @method
13766      */
13767     autoSize: Roo.emptyFn,
13768     // private
13769     monitorTab : true,
13770     // private
13771     deferHeight : true,
13772
13773     
13774     actionMode : 'wrap',
13775     
13776     caret : false,
13777     
13778     
13779     getAutoCreate : function(){
13780        
13781         var align = this.labelAlign || this.parentLabelAlign();
13782         
13783         var id = Roo.id();
13784         
13785         var cfg = {
13786             cls: 'form-group' //input-group
13787         };
13788         
13789         
13790         var input =  {
13791             tag: 'input',
13792             id : id,
13793             type : this.inputType,
13794             cls : 'form-control',
13795             autocomplete: 'new-password',
13796             placeholder : this.placeholder || '' 
13797             
13798         };
13799         if (this.name) {
13800             input.name = this.name;
13801         }
13802         if (this.size) {
13803             input.cls += ' input-' + this.size;
13804         }
13805         
13806         if (this.disabled) {
13807             input.disabled=true;
13808         }
13809         
13810         var inputblock = input;
13811         
13812         if(this.hasFeedback && !this.allowBlank){
13813             
13814             var feedback = {
13815                 tag: 'span',
13816                 cls: 'glyphicon form-control-feedback'
13817             };
13818             
13819             if(this.removable && !this.editable  ){
13820                 inputblock = {
13821                     cls : 'has-feedback',
13822                     cn :  [
13823                         inputblock,
13824                         {
13825                             tag: 'button',
13826                             html : 'x',
13827                             cls : 'roo-combo-removable-btn close'
13828                         },
13829                         feedback
13830                     ] 
13831                 };
13832             } else {
13833                 inputblock = {
13834                     cls : 'has-feedback',
13835                     cn :  [
13836                         inputblock,
13837                         feedback
13838                     ] 
13839                 };
13840             }
13841
13842         } else {
13843             if(this.removable && !this.editable ){
13844                 inputblock = {
13845                     cls : 'roo-removable',
13846                     cn :  [
13847                         inputblock,
13848                         {
13849                             tag: 'button',
13850                             html : 'x',
13851                             cls : 'roo-combo-removable-btn close'
13852                         }
13853                     ] 
13854                 };
13855             }
13856         }
13857         
13858         if (this.before || this.after) {
13859             
13860             inputblock = {
13861                 cls : 'input-group',
13862                 cn :  [] 
13863             };
13864             if (this.before) {
13865                 inputblock.cn.push({
13866                     tag :'span',
13867                     cls : 'input-group-addon input-group-prepend input-group-text',
13868                     html : this.before
13869                 });
13870             }
13871             
13872             inputblock.cn.push(input);
13873             
13874             if(this.hasFeedback && !this.allowBlank){
13875                 inputblock.cls += ' has-feedback';
13876                 inputblock.cn.push(feedback);
13877             }
13878             
13879             if (this.after) {
13880                 inputblock.cn.push({
13881                     tag :'span',
13882                     cls : 'input-group-addon input-group-append input-group-text',
13883                     html : this.after
13884                 });
13885             }
13886             
13887         };
13888         
13889       
13890         
13891         var ibwrap = inputblock;
13892         
13893         if(this.multiple){
13894             ibwrap = {
13895                 tag: 'ul',
13896                 cls: 'roo-select2-choices',
13897                 cn:[
13898                     {
13899                         tag: 'li',
13900                         cls: 'roo-select2-search-field',
13901                         cn: [
13902
13903                             inputblock
13904                         ]
13905                     }
13906                 ]
13907             };
13908                 
13909         }
13910         
13911         var combobox = {
13912             cls: 'roo-select2-container input-group',
13913             cn: [
13914                  {
13915                     tag: 'input',
13916                     type : 'hidden',
13917                     cls: 'form-hidden-field'
13918                 },
13919                 ibwrap
13920             ]
13921         };
13922         
13923         if(!this.multiple && this.showToggleBtn){
13924             
13925             var caret = {
13926                         tag: 'span',
13927                         cls: 'caret'
13928              };
13929             if (this.caret != false) {
13930                 caret = {
13931                      tag: 'i',
13932                      cls: 'fa fa-' + this.caret
13933                 };
13934                 
13935             }
13936             
13937             combobox.cn.push({
13938                 tag :'span',
13939                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13940                 cn : [
13941                     Roo.bootstrap.version == 3 ? caret : '',
13942                     {
13943                         tag: 'span',
13944                         cls: 'combobox-clear',
13945                         cn  : [
13946                             {
13947                                 tag : 'i',
13948                                 cls: 'icon-remove'
13949                             }
13950                         ]
13951                     }
13952                 ]
13953
13954             })
13955         }
13956         
13957         if(this.multiple){
13958             combobox.cls += ' roo-select2-container-multi';
13959         }
13960          var indicator = {
13961             tag : 'i',
13962             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13963             tooltip : 'This field is required'
13964         };
13965         if (Roo.bootstrap.version == 4) {
13966             indicator = {
13967                 tag : 'i',
13968                 style : 'display:none'
13969             };
13970         }
13971         
13972         
13973         if (align ==='left' && this.fieldLabel.length) {
13974             
13975             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13976
13977             cfg.cn = [
13978                 indicator,
13979                 {
13980                     tag: 'label',
13981                     'for' :  id,
13982                     cls : 'control-label',
13983                     html : this.fieldLabel
13984
13985                 },
13986                 {
13987                     cls : "", 
13988                     cn: [
13989                         combobox
13990                     ]
13991                 }
13992
13993             ];
13994             
13995             var labelCfg = cfg.cn[1];
13996             var contentCfg = cfg.cn[2];
13997             
13998             if(this.indicatorpos == 'right'){
13999                 cfg.cn = [
14000                     {
14001                         tag: 'label',
14002                         'for' :  id,
14003                         cls : 'control-label',
14004                         cn : [
14005                             {
14006                                 tag : 'span',
14007                                 html : this.fieldLabel
14008                             },
14009                             indicator
14010                         ]
14011                     },
14012                     {
14013                         cls : "", 
14014                         cn: [
14015                             combobox
14016                         ]
14017                     }
14018
14019                 ];
14020                 
14021                 labelCfg = cfg.cn[0];
14022                 contentCfg = cfg.cn[1];
14023             }
14024             
14025             if(this.labelWidth > 12){
14026                 labelCfg.style = "width: " + this.labelWidth + 'px';
14027             }
14028             
14029             if(this.labelWidth < 13 && this.labelmd == 0){
14030                 this.labelmd = this.labelWidth;
14031             }
14032             
14033             if(this.labellg > 0){
14034                 labelCfg.cls += ' col-lg-' + this.labellg;
14035                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14036             }
14037             
14038             if(this.labelmd > 0){
14039                 labelCfg.cls += ' col-md-' + this.labelmd;
14040                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14041             }
14042             
14043             if(this.labelsm > 0){
14044                 labelCfg.cls += ' col-sm-' + this.labelsm;
14045                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14046             }
14047             
14048             if(this.labelxs > 0){
14049                 labelCfg.cls += ' col-xs-' + this.labelxs;
14050                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14051             }
14052             
14053         } else if ( this.fieldLabel.length) {
14054 //                Roo.log(" label");
14055             cfg.cn = [
14056                 indicator,
14057                {
14058                    tag: 'label',
14059                    //cls : 'input-group-addon',
14060                    html : this.fieldLabel
14061
14062                },
14063
14064                combobox
14065
14066             ];
14067             
14068             if(this.indicatorpos == 'right'){
14069                 
14070                 cfg.cn = [
14071                     {
14072                        tag: 'label',
14073                        cn : [
14074                            {
14075                                tag : 'span',
14076                                html : this.fieldLabel
14077                            },
14078                            indicator
14079                        ]
14080
14081                     },
14082                     combobox
14083
14084                 ];
14085
14086             }
14087
14088         } else {
14089             
14090 //                Roo.log(" no label && no align");
14091                 cfg = combobox
14092                      
14093                 
14094         }
14095         
14096         var settings=this;
14097         ['xs','sm','md','lg'].map(function(size){
14098             if (settings[size]) {
14099                 cfg.cls += ' col-' + size + '-' + settings[size];
14100             }
14101         });
14102         
14103         return cfg;
14104         
14105     },
14106     
14107     
14108     
14109     // private
14110     onResize : function(w, h){
14111 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14112 //        if(typeof w == 'number'){
14113 //            var x = w - this.trigger.getWidth();
14114 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14115 //            this.trigger.setStyle('left', x+'px');
14116 //        }
14117     },
14118
14119     // private
14120     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14121
14122     // private
14123     getResizeEl : function(){
14124         return this.inputEl();
14125     },
14126
14127     // private
14128     getPositionEl : function(){
14129         return this.inputEl();
14130     },
14131
14132     // private
14133     alignErrorIcon : function(){
14134         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14135     },
14136
14137     // private
14138     initEvents : function(){
14139         
14140         this.createList();
14141         
14142         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14143         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14144         if(!this.multiple && this.showToggleBtn){
14145             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14146             if(this.hideTrigger){
14147                 this.trigger.setDisplayed(false);
14148             }
14149             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14150         }
14151         
14152         if(this.multiple){
14153             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14154         }
14155         
14156         if(this.removable && !this.editable && !this.tickable){
14157             var close = this.closeTriggerEl();
14158             
14159             if(close){
14160                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14161                 close.on('click', this.removeBtnClick, this, close);
14162             }
14163         }
14164         
14165         //this.trigger.addClassOnOver('x-form-trigger-over');
14166         //this.trigger.addClassOnClick('x-form-trigger-click');
14167         
14168         //if(!this.width){
14169         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14170         //}
14171     },
14172     
14173     closeTriggerEl : function()
14174     {
14175         var close = this.el.select('.roo-combo-removable-btn', true).first();
14176         return close ? close : false;
14177     },
14178     
14179     removeBtnClick : function(e, h, el)
14180     {
14181         e.preventDefault();
14182         
14183         if(this.fireEvent("remove", this) !== false){
14184             this.reset();
14185             this.fireEvent("afterremove", this)
14186         }
14187     },
14188     
14189     createList : function()
14190     {
14191         this.list = Roo.get(document.body).createChild({
14192             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14193             cls: 'typeahead typeahead-long dropdown-menu shadow',
14194             style: 'display:none'
14195         });
14196         
14197         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14198         
14199     },
14200
14201     // private
14202     initTrigger : function(){
14203        
14204     },
14205
14206     // private
14207     onDestroy : function(){
14208         if(this.trigger){
14209             this.trigger.removeAllListeners();
14210           //  this.trigger.remove();
14211         }
14212         //if(this.wrap){
14213         //    this.wrap.remove();
14214         //}
14215         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14216     },
14217
14218     // private
14219     onFocus : function(){
14220         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14221         /*
14222         if(!this.mimicing){
14223             this.wrap.addClass('x-trigger-wrap-focus');
14224             this.mimicing = true;
14225             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14226             if(this.monitorTab){
14227                 this.el.on("keydown", this.checkTab, this);
14228             }
14229         }
14230         */
14231     },
14232
14233     // private
14234     checkTab : function(e){
14235         if(e.getKey() == e.TAB){
14236             this.triggerBlur();
14237         }
14238     },
14239
14240     // private
14241     onBlur : function(){
14242         // do nothing
14243     },
14244
14245     // private
14246     mimicBlur : function(e, t){
14247         /*
14248         if(!this.wrap.contains(t) && this.validateBlur()){
14249             this.triggerBlur();
14250         }
14251         */
14252     },
14253
14254     // private
14255     triggerBlur : function(){
14256         this.mimicing = false;
14257         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14258         if(this.monitorTab){
14259             this.el.un("keydown", this.checkTab, this);
14260         }
14261         //this.wrap.removeClass('x-trigger-wrap-focus');
14262         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14263     },
14264
14265     // private
14266     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14267     validateBlur : function(e, t){
14268         return true;
14269     },
14270
14271     // private
14272     onDisable : function(){
14273         this.inputEl().dom.disabled = true;
14274         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14275         //if(this.wrap){
14276         //    this.wrap.addClass('x-item-disabled');
14277         //}
14278     },
14279
14280     // private
14281     onEnable : function(){
14282         this.inputEl().dom.disabled = false;
14283         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14284         //if(this.wrap){
14285         //    this.el.removeClass('x-item-disabled');
14286         //}
14287     },
14288
14289     // private
14290     onShow : function(){
14291         var ae = this.getActionEl();
14292         
14293         if(ae){
14294             ae.dom.style.display = '';
14295             ae.dom.style.visibility = 'visible';
14296         }
14297     },
14298
14299     // private
14300     
14301     onHide : function(){
14302         var ae = this.getActionEl();
14303         ae.dom.style.display = 'none';
14304     },
14305
14306     /**
14307      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14308      * by an implementing function.
14309      * @method
14310      * @param {EventObject} e
14311      */
14312     onTriggerClick : Roo.emptyFn
14313 });
14314  
14315 /*
14316 * Licence: LGPL
14317 */
14318
14319 /**
14320  * @class Roo.bootstrap.form.CardUploader
14321  * @extends Roo.bootstrap.Button
14322  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14323  * @cfg {Number} errorTimeout default 3000
14324  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14325  * @cfg {Array}  html The button text.
14326
14327  *
14328  * @constructor
14329  * Create a new CardUploader
14330  * @param {Object} config The config object
14331  */
14332
14333 Roo.bootstrap.form.CardUploader = function(config){
14334     
14335  
14336     
14337     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14338     
14339     
14340     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14341         return r.data.id
14342      });
14343     
14344      this.addEvents({
14345          // raw events
14346         /**
14347          * @event preview
14348          * When a image is clicked on - and needs to display a slideshow or similar..
14349          * @param {Roo.bootstrap.Card} this
14350          * @param {Object} The image information data 
14351          *
14352          */
14353         'preview' : true,
14354          /**
14355          * @event download
14356          * When a the download link is clicked
14357          * @param {Roo.bootstrap.Card} this
14358          * @param {Object} The image information data  contains 
14359          */
14360         'download' : true
14361         
14362     });
14363 };
14364  
14365 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14366     
14367      
14368     errorTimeout : 3000,
14369      
14370     images : false,
14371    
14372     fileCollection : false,
14373     allowBlank : true,
14374     
14375     getAutoCreate : function()
14376     {
14377         
14378         var cfg =  {
14379             cls :'form-group' ,
14380             cn : [
14381                
14382                 {
14383                     tag: 'label',
14384                    //cls : 'input-group-addon',
14385                     html : this.fieldLabel
14386
14387                 },
14388
14389                 {
14390                     tag: 'input',
14391                     type : 'hidden',
14392                     name : this.name,
14393                     value : this.value,
14394                     cls : 'd-none  form-control'
14395                 },
14396                 
14397                 {
14398                     tag: 'input',
14399                     multiple : 'multiple',
14400                     type : 'file',
14401                     cls : 'd-none  roo-card-upload-selector'
14402                 },
14403                 
14404                 {
14405                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14406                 },
14407                 {
14408                     cls : 'card-columns roo-card-uploader-container'
14409                 }
14410
14411             ]
14412         };
14413            
14414          
14415         return cfg;
14416     },
14417     
14418     getChildContainer : function() /// what children are added to.
14419     {
14420         return this.containerEl;
14421     },
14422    
14423     getButtonContainer : function() /// what children are added to.
14424     {
14425         return this.el.select(".roo-card-uploader-button-container").first();
14426     },
14427    
14428     initEvents : function()
14429     {
14430         
14431         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14432         
14433         var t = this;
14434         this.addxtype({
14435             xns: Roo.bootstrap,
14436
14437             xtype : 'Button',
14438             container_method : 'getButtonContainer' ,            
14439             html :  this.html, // fix changable?
14440             cls : 'w-100 ',
14441             listeners : {
14442                 'click' : function(btn, e) {
14443                     t.onClick(e);
14444                 }
14445             }
14446         });
14447         
14448         
14449         
14450         
14451         this.urlAPI = (window.createObjectURL && window) || 
14452                                 (window.URL && URL.revokeObjectURL && URL) || 
14453                                 (window.webkitURL && webkitURL);
14454                         
14455          
14456          
14457          
14458         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14459         
14460         this.selectorEl.on('change', this.onFileSelected, this);
14461         if (this.images) {
14462             var t = this;
14463             this.images.forEach(function(img) {
14464                 t.addCard(img)
14465             });
14466             this.images = false;
14467         }
14468         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14469          
14470        
14471     },
14472     
14473    
14474     onClick : function(e)
14475     {
14476         e.preventDefault();
14477          
14478         this.selectorEl.dom.click();
14479          
14480     },
14481     
14482     onFileSelected : function(e)
14483     {
14484         e.preventDefault();
14485         
14486         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14487             return;
14488         }
14489         
14490         Roo.each(this.selectorEl.dom.files, function(file){    
14491             this.addFile(file);
14492         }, this);
14493          
14494     },
14495     
14496       
14497     
14498       
14499     
14500     addFile : function(file)
14501     {
14502            
14503         if(typeof(file) === 'string'){
14504             throw "Add file by name?"; // should not happen
14505             return;
14506         }
14507         
14508         if(!file || !this.urlAPI){
14509             return;
14510         }
14511         
14512         // file;
14513         // file.type;
14514         
14515         var _this = this;
14516         
14517         
14518         var url = _this.urlAPI.createObjectURL( file);
14519            
14520         this.addCard({
14521             id : Roo.bootstrap.form.CardUploader.ID--,
14522             is_uploaded : false,
14523             src : url,
14524             srcfile : file,
14525             title : file.name,
14526             mimetype : file.type,
14527             preview : false,
14528             is_deleted : 0
14529         });
14530         
14531     },
14532     
14533     /**
14534      * addCard - add an Attachment to the uploader
14535      * @param data - the data about the image to upload
14536      *
14537      * {
14538           id : 123
14539           title : "Title of file",
14540           is_uploaded : false,
14541           src : "http://.....",
14542           srcfile : { the File upload object },
14543           mimetype : file.type,
14544           preview : false,
14545           is_deleted : 0
14546           .. any other data...
14547         }
14548      *
14549      * 
14550     */
14551     
14552     addCard : function (data)
14553     {
14554         // hidden input element?
14555         // if the file is not an image...
14556         //then we need to use something other that and header_image
14557         var t = this;
14558         //   remove.....
14559         var footer = [
14560             {
14561                 xns : Roo.bootstrap,
14562                 xtype : 'CardFooter',
14563                  items: [
14564                     {
14565                         xns : Roo.bootstrap,
14566                         xtype : 'Element',
14567                         cls : 'd-flex',
14568                         items : [
14569                             
14570                             {
14571                                 xns : Roo.bootstrap,
14572                                 xtype : 'Button',
14573                                 html : String.format("<small>{0}</small>", data.title),
14574                                 cls : 'col-10 text-left',
14575                                 size: 'sm',
14576                                 weight: 'link',
14577                                 fa : 'download',
14578                                 listeners : {
14579                                     click : function() {
14580                                      
14581                                         t.fireEvent( "download", t, data );
14582                                     }
14583                                 }
14584                             },
14585                           
14586                             {
14587                                 xns : Roo.bootstrap,
14588                                 xtype : 'Button',
14589                                 style: 'max-height: 28px; ',
14590                                 size : 'sm',
14591                                 weight: 'danger',
14592                                 cls : 'col-2',
14593                                 fa : 'times',
14594                                 listeners : {
14595                                     click : function() {
14596                                         t.removeCard(data.id)
14597                                     }
14598                                 }
14599                             }
14600                         ]
14601                     }
14602                     
14603                 ] 
14604             }
14605             
14606         ];
14607         
14608         var cn = this.addxtype(
14609             {
14610                  
14611                 xns : Roo.bootstrap,
14612                 xtype : 'Card',
14613                 closeable : true,
14614                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14615                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14616                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14617                 data : data,
14618                 html : false,
14619                  
14620                 items : footer,
14621                 initEvents : function() {
14622                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14623                     var card = this;
14624                     this.imgEl = this.el.select('.card-img-top').first();
14625                     if (this.imgEl) {
14626                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14627                         this.imgEl.set({ 'pointer' : 'cursor' });
14628                                   
14629                     }
14630                     this.getCardFooter().addClass('p-1');
14631                     
14632                   
14633                 }
14634                 
14635             }
14636         );
14637         // dont' really need ot update items.
14638         // this.items.push(cn);
14639         this.fileCollection.add(cn);
14640         
14641         if (!data.srcfile) {
14642             this.updateInput();
14643             return;
14644         }
14645             
14646         var _t = this;
14647         var reader = new FileReader();
14648         reader.addEventListener("load", function() {  
14649             data.srcdata =  reader.result;
14650             _t.updateInput();
14651         });
14652         reader.readAsDataURL(data.srcfile);
14653         
14654         
14655         
14656     },
14657     removeCard : function(id)
14658     {
14659         
14660         var card  = this.fileCollection.get(id);
14661         card.data.is_deleted = 1;
14662         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14663         //this.fileCollection.remove(card);
14664         //this.items = this.items.filter(function(e) { return e != card });
14665         // dont' really need ot update items.
14666         card.el.dom.parentNode.removeChild(card.el.dom);
14667         this.updateInput();
14668
14669         
14670     },
14671     reset: function()
14672     {
14673         this.fileCollection.each(function(card) {
14674             if (card.el.dom && card.el.dom.parentNode) {
14675                 card.el.dom.parentNode.removeChild(card.el.dom);
14676             }
14677         });
14678         this.fileCollection.clear();
14679         this.updateInput();
14680     },
14681     
14682     updateInput : function()
14683     {
14684          var data = [];
14685         this.fileCollection.each(function(e) {
14686             data.push(e.data);
14687             
14688         });
14689         this.inputEl().dom.value = JSON.stringify(data);
14690         
14691         
14692         
14693     }
14694     
14695     
14696 });
14697
14698
14699 Roo.bootstrap.form.CardUploader.ID = -1;/*
14700  * Based on:
14701  * Ext JS Library 1.1.1
14702  * Copyright(c) 2006-2007, Ext JS, LLC.
14703  *
14704  * Originally Released Under LGPL - original licence link has changed is not relivant.
14705  *
14706  * Fork - LGPL
14707  * <script type="text/javascript">
14708  */
14709
14710
14711 /**
14712  * @class Roo.data.SortTypes
14713  * @static
14714  * Defines the default sorting (casting?) comparison functions used when sorting data.
14715  */
14716 Roo.data.SortTypes = {
14717     /**
14718      * Default sort that does nothing
14719      * @param {Mixed} s The value being converted
14720      * @return {Mixed} The comparison value
14721      */
14722     none : function(s){
14723         return s;
14724     },
14725     
14726     /**
14727      * The regular expression used to strip tags
14728      * @type {RegExp}
14729      * @property
14730      */
14731     stripTagsRE : /<\/?[^>]+>/gi,
14732     
14733     /**
14734      * Strips all HTML tags to sort on text only
14735      * @param {Mixed} s The value being converted
14736      * @return {String} The comparison value
14737      */
14738     asText : function(s){
14739         return String(s).replace(this.stripTagsRE, "");
14740     },
14741     
14742     /**
14743      * Strips all HTML tags to sort on text only - Case insensitive
14744      * @param {Mixed} s The value being converted
14745      * @return {String} The comparison value
14746      */
14747     asUCText : function(s){
14748         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14749     },
14750     
14751     /**
14752      * Case insensitive string
14753      * @param {Mixed} s The value being converted
14754      * @return {String} The comparison value
14755      */
14756     asUCString : function(s) {
14757         return String(s).toUpperCase();
14758     },
14759     
14760     /**
14761      * Date sorting
14762      * @param {Mixed} s The value being converted
14763      * @return {Number} The comparison value
14764      */
14765     asDate : function(s) {
14766         if(!s){
14767             return 0;
14768         }
14769         if(s instanceof Date){
14770             return s.getTime();
14771         }
14772         return Date.parse(String(s));
14773     },
14774     
14775     /**
14776      * Float sorting
14777      * @param {Mixed} s The value being converted
14778      * @return {Float} The comparison value
14779      */
14780     asFloat : function(s) {
14781         var val = parseFloat(String(s).replace(/,/g, ""));
14782         if(isNaN(val)) {
14783             val = 0;
14784         }
14785         return val;
14786     },
14787     
14788     /**
14789      * Integer sorting
14790      * @param {Mixed} s The value being converted
14791      * @return {Number} The comparison value
14792      */
14793     asInt : function(s) {
14794         var val = parseInt(String(s).replace(/,/g, ""));
14795         if(isNaN(val)) {
14796             val = 0;
14797         }
14798         return val;
14799     }
14800 };/*
14801  * Based on:
14802  * Ext JS Library 1.1.1
14803  * Copyright(c) 2006-2007, Ext JS, LLC.
14804  *
14805  * Originally Released Under LGPL - original licence link has changed is not relivant.
14806  *
14807  * Fork - LGPL
14808  * <script type="text/javascript">
14809  */
14810
14811 /**
14812 * @class Roo.data.Record
14813  * Instances of this class encapsulate both record <em>definition</em> information, and record
14814  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14815  * to access Records cached in an {@link Roo.data.Store} object.<br>
14816  * <p>
14817  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14818  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14819  * objects.<br>
14820  * <p>
14821  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14822  * @constructor
14823  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14824  * {@link #create}. The parameters are the same.
14825  * @param {Array} data An associative Array of data values keyed by the field name.
14826  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14827  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14828  * not specified an integer id is generated.
14829  */
14830 Roo.data.Record = function(data, id){
14831     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14832     this.data = data;
14833 };
14834
14835 /**
14836  * Generate a constructor for a specific record layout.
14837  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14838  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14839  * Each field definition object may contain the following properties: <ul>
14840  * <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,
14841  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14842  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14843  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14844  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14845  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14846  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14847  * this may be omitted.</p></li>
14848  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14849  * <ul><li>auto (Default, implies no conversion)</li>
14850  * <li>string</li>
14851  * <li>int</li>
14852  * <li>float</li>
14853  * <li>boolean</li>
14854  * <li>date</li></ul></p></li>
14855  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14856  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14857  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14858  * by the Reader into an object that will be stored in the Record. It is passed the
14859  * following parameters:<ul>
14860  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14861  * </ul></p></li>
14862  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14863  * </ul>
14864  * <br>usage:<br><pre><code>
14865 var TopicRecord = Roo.data.Record.create(
14866     {name: 'title', mapping: 'topic_title'},
14867     {name: 'author', mapping: 'username'},
14868     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14869     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14870     {name: 'lastPoster', mapping: 'user2'},
14871     {name: 'excerpt', mapping: 'post_text'}
14872 );
14873
14874 var myNewRecord = new TopicRecord({
14875     title: 'Do my job please',
14876     author: 'noobie',
14877     totalPosts: 1,
14878     lastPost: new Date(),
14879     lastPoster: 'Animal',
14880     excerpt: 'No way dude!'
14881 });
14882 myStore.add(myNewRecord);
14883 </code></pre>
14884  * @method create
14885  * @static
14886  */
14887 Roo.data.Record.create = function(o){
14888     var f = function(){
14889         f.superclass.constructor.apply(this, arguments);
14890     };
14891     Roo.extend(f, Roo.data.Record);
14892     var p = f.prototype;
14893     p.fields = new Roo.util.MixedCollection(false, function(field){
14894         return field.name;
14895     });
14896     for(var i = 0, len = o.length; i < len; i++){
14897         p.fields.add(new Roo.data.Field(o[i]));
14898     }
14899     f.getField = function(name){
14900         return p.fields.get(name);  
14901     };
14902     return f;
14903 };
14904
14905 Roo.data.Record.AUTO_ID = 1000;
14906 Roo.data.Record.EDIT = 'edit';
14907 Roo.data.Record.REJECT = 'reject';
14908 Roo.data.Record.COMMIT = 'commit';
14909
14910 Roo.data.Record.prototype = {
14911     /**
14912      * Readonly flag - true if this record has been modified.
14913      * @type Boolean
14914      */
14915     dirty : false,
14916     editing : false,
14917     error: null,
14918     modified: null,
14919
14920     // private
14921     join : function(store){
14922         this.store = store;
14923     },
14924
14925     /**
14926      * Set the named field to the specified value.
14927      * @param {String} name The name of the field to set.
14928      * @param {Object} value The value to set the field to.
14929      */
14930     set : function(name, value){
14931         if(this.data[name] == value){
14932             return;
14933         }
14934         this.dirty = true;
14935         if(!this.modified){
14936             this.modified = {};
14937         }
14938         if(typeof this.modified[name] == 'undefined'){
14939             this.modified[name] = this.data[name];
14940         }
14941         this.data[name] = value;
14942         if(!this.editing && this.store){
14943             this.store.afterEdit(this);
14944         }       
14945     },
14946
14947     /**
14948      * Get the value of the named field.
14949      * @param {String} name The name of the field to get the value of.
14950      * @return {Object} The value of the field.
14951      */
14952     get : function(name){
14953         return this.data[name]; 
14954     },
14955
14956     // private
14957     beginEdit : function(){
14958         this.editing = true;
14959         this.modified = {}; 
14960     },
14961
14962     // private
14963     cancelEdit : function(){
14964         this.editing = false;
14965         delete this.modified;
14966     },
14967
14968     // private
14969     endEdit : function(){
14970         this.editing = false;
14971         if(this.dirty && this.store){
14972             this.store.afterEdit(this);
14973         }
14974     },
14975
14976     /**
14977      * Usually called by the {@link Roo.data.Store} which owns the Record.
14978      * Rejects all changes made to the Record since either creation, or the last commit operation.
14979      * Modified fields are reverted to their original values.
14980      * <p>
14981      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14982      * of reject operations.
14983      */
14984     reject : function(){
14985         var m = this.modified;
14986         for(var n in m){
14987             if(typeof m[n] != "function"){
14988                 this.data[n] = m[n];
14989             }
14990         }
14991         this.dirty = false;
14992         delete this.modified;
14993         this.editing = false;
14994         if(this.store){
14995             this.store.afterReject(this);
14996         }
14997     },
14998
14999     /**
15000      * Usually called by the {@link Roo.data.Store} which owns the Record.
15001      * Commits all changes made to the Record since either creation, or the last commit operation.
15002      * <p>
15003      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15004      * of commit operations.
15005      */
15006     commit : function(){
15007         this.dirty = false;
15008         delete this.modified;
15009         this.editing = false;
15010         if(this.store){
15011             this.store.afterCommit(this);
15012         }
15013     },
15014
15015     // private
15016     hasError : function(){
15017         return this.error != null;
15018     },
15019
15020     // private
15021     clearError : function(){
15022         this.error = null;
15023     },
15024
15025     /**
15026      * Creates a copy of this record.
15027      * @param {String} id (optional) A new record id if you don't want to use this record's id
15028      * @return {Record}
15029      */
15030     copy : function(newId) {
15031         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15032     }
15033 };/*
15034  * Based on:
15035  * Ext JS Library 1.1.1
15036  * Copyright(c) 2006-2007, Ext JS, LLC.
15037  *
15038  * Originally Released Under LGPL - original licence link has changed is not relivant.
15039  *
15040  * Fork - LGPL
15041  * <script type="text/javascript">
15042  */
15043
15044
15045
15046 /**
15047  * @class Roo.data.Store
15048  * @extends Roo.util.Observable
15049  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15050  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15051  * <p>
15052  * 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
15053  * has no knowledge of the format of the data returned by the Proxy.<br>
15054  * <p>
15055  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15056  * instances from the data object. These records are cached and made available through accessor functions.
15057  * @constructor
15058  * Creates a new Store.
15059  * @param {Object} config A config object containing the objects needed for the Store to access data,
15060  * and read the data into Records.
15061  */
15062 Roo.data.Store = function(config){
15063     this.data = new Roo.util.MixedCollection(false);
15064     this.data.getKey = function(o){
15065         return o.id;
15066     };
15067     this.baseParams = {};
15068     // private
15069     this.paramNames = {
15070         "start" : "start",
15071         "limit" : "limit",
15072         "sort" : "sort",
15073         "dir" : "dir",
15074         "multisort" : "_multisort"
15075     };
15076
15077     if(config && config.data){
15078         this.inlineData = config.data;
15079         delete config.data;
15080     }
15081
15082     Roo.apply(this, config);
15083     
15084     if(this.reader){ // reader passed
15085         this.reader = Roo.factory(this.reader, Roo.data);
15086         this.reader.xmodule = this.xmodule || false;
15087         if(!this.recordType){
15088             this.recordType = this.reader.recordType;
15089         }
15090         if(this.reader.onMetaChange){
15091             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15092         }
15093     }
15094
15095     if(this.recordType){
15096         this.fields = this.recordType.prototype.fields;
15097     }
15098     this.modified = [];
15099
15100     this.addEvents({
15101         /**
15102          * @event datachanged
15103          * Fires when the data cache has changed, and a widget which is using this Store
15104          * as a Record cache should refresh its view.
15105          * @param {Store} this
15106          */
15107         datachanged : true,
15108         /**
15109          * @event metachange
15110          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15111          * @param {Store} this
15112          * @param {Object} meta The JSON metadata
15113          */
15114         metachange : true,
15115         /**
15116          * @event add
15117          * Fires when Records have been added to the Store
15118          * @param {Store} this
15119          * @param {Roo.data.Record[]} records The array of Records added
15120          * @param {Number} index The index at which the record(s) were added
15121          */
15122         add : true,
15123         /**
15124          * @event remove
15125          * Fires when a Record has been removed from the Store
15126          * @param {Store} this
15127          * @param {Roo.data.Record} record The Record that was removed
15128          * @param {Number} index The index at which the record was removed
15129          */
15130         remove : true,
15131         /**
15132          * @event update
15133          * Fires when a Record has been updated
15134          * @param {Store} this
15135          * @param {Roo.data.Record} record The Record that was updated
15136          * @param {String} operation The update operation being performed.  Value may be one of:
15137          * <pre><code>
15138  Roo.data.Record.EDIT
15139  Roo.data.Record.REJECT
15140  Roo.data.Record.COMMIT
15141          * </code></pre>
15142          */
15143         update : true,
15144         /**
15145          * @event clear
15146          * Fires when the data cache has been cleared.
15147          * @param {Store} this
15148          */
15149         clear : true,
15150         /**
15151          * @event beforeload
15152          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15153          * the load action will be canceled.
15154          * @param {Store} this
15155          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15156          */
15157         beforeload : true,
15158         /**
15159          * @event beforeloadadd
15160          * Fires after a new set of Records has been loaded.
15161          * @param {Store} this
15162          * @param {Roo.data.Record[]} records The Records that were loaded
15163          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15164          */
15165         beforeloadadd : true,
15166         /**
15167          * @event load
15168          * Fires after a new set of Records has been loaded, before they are added to the store.
15169          * @param {Store} this
15170          * @param {Roo.data.Record[]} records The Records that were loaded
15171          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15172          * @params {Object} return from reader
15173          */
15174         load : true,
15175         /**
15176          * @event loadexception
15177          * Fires if an exception occurs in the Proxy during loading.
15178          * Called with the signature of the Proxy's "loadexception" event.
15179          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15180          * 
15181          * @param {Proxy} 
15182          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15183          * @param {Object} load options 
15184          * @param {Object} jsonData from your request (normally this contains the Exception)
15185          */
15186         loadexception : true
15187     });
15188     
15189     if(this.proxy){
15190         this.proxy = Roo.factory(this.proxy, Roo.data);
15191         this.proxy.xmodule = this.xmodule || false;
15192         this.relayEvents(this.proxy,  ["loadexception"]);
15193     }
15194     this.sortToggle = {};
15195     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15196
15197     Roo.data.Store.superclass.constructor.call(this);
15198
15199     if(this.inlineData){
15200         this.loadData(this.inlineData);
15201         delete this.inlineData;
15202     }
15203 };
15204
15205 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15206      /**
15207     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15208     * without a remote query - used by combo/forms at present.
15209     */
15210     
15211     /**
15212     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15213     */
15214     /**
15215     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15216     */
15217     /**
15218     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15219     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15220     */
15221     /**
15222     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15223     * on any HTTP request
15224     */
15225     /**
15226     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15227     */
15228     /**
15229     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15230     */
15231     multiSort: false,
15232     /**
15233     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15234     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15235     */
15236     remoteSort : false,
15237
15238     /**
15239     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15240      * loaded or when a record is removed. (defaults to false).
15241     */
15242     pruneModifiedRecords : false,
15243
15244     // private
15245     lastOptions : null,
15246
15247     /**
15248      * Add Records to the Store and fires the add event.
15249      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15250      */
15251     add : function(records){
15252         records = [].concat(records);
15253         for(var i = 0, len = records.length; i < len; i++){
15254             records[i].join(this);
15255         }
15256         var index = this.data.length;
15257         this.data.addAll(records);
15258         this.fireEvent("add", this, records, index);
15259     },
15260
15261     /**
15262      * Remove a Record from the Store and fires the remove event.
15263      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15264      */
15265     remove : function(record){
15266         var index = this.data.indexOf(record);
15267         this.data.removeAt(index);
15268  
15269         if(this.pruneModifiedRecords){
15270             this.modified.remove(record);
15271         }
15272         this.fireEvent("remove", this, record, index);
15273     },
15274
15275     /**
15276      * Remove all Records from the Store and fires the clear event.
15277      */
15278     removeAll : function(){
15279         this.data.clear();
15280         if(this.pruneModifiedRecords){
15281             this.modified = [];
15282         }
15283         this.fireEvent("clear", this);
15284     },
15285
15286     /**
15287      * Inserts Records to the Store at the given index and fires the add event.
15288      * @param {Number} index The start index at which to insert the passed Records.
15289      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15290      */
15291     insert : function(index, records){
15292         records = [].concat(records);
15293         for(var i = 0, len = records.length; i < len; i++){
15294             this.data.insert(index, records[i]);
15295             records[i].join(this);
15296         }
15297         this.fireEvent("add", this, records, index);
15298     },
15299
15300     /**
15301      * Get the index within the cache of the passed Record.
15302      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15303      * @return {Number} The index of the passed Record. Returns -1 if not found.
15304      */
15305     indexOf : function(record){
15306         return this.data.indexOf(record);
15307     },
15308
15309     /**
15310      * Get the index within the cache of the Record with the passed id.
15311      * @param {String} id The id of the Record to find.
15312      * @return {Number} The index of the Record. Returns -1 if not found.
15313      */
15314     indexOfId : function(id){
15315         return this.data.indexOfKey(id);
15316     },
15317
15318     /**
15319      * Get the Record with the specified id.
15320      * @param {String} id The id of the Record to find.
15321      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15322      */
15323     getById : function(id){
15324         return this.data.key(id);
15325     },
15326
15327     /**
15328      * Get the Record at the specified index.
15329      * @param {Number} index The index of the Record to find.
15330      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15331      */
15332     getAt : function(index){
15333         return this.data.itemAt(index);
15334     },
15335
15336     /**
15337      * Returns a range of Records between specified indices.
15338      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15339      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15340      * @return {Roo.data.Record[]} An array of Records
15341      */
15342     getRange : function(start, end){
15343         return this.data.getRange(start, end);
15344     },
15345
15346     // private
15347     storeOptions : function(o){
15348         o = Roo.apply({}, o);
15349         delete o.callback;
15350         delete o.scope;
15351         this.lastOptions = o;
15352     },
15353
15354     /**
15355      * Loads the Record cache from the configured Proxy using the configured Reader.
15356      * <p>
15357      * If using remote paging, then the first load call must specify the <em>start</em>
15358      * and <em>limit</em> properties in the options.params property to establish the initial
15359      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15360      * <p>
15361      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15362      * and this call will return before the new data has been loaded. Perform any post-processing
15363      * in a callback function, or in a "load" event handler.</strong>
15364      * <p>
15365      * @param {Object} options An object containing properties which control loading options:<ul>
15366      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15367      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15368      * <pre>
15369                 {
15370                     data : data,  // array of key=>value data like JsonReader
15371                     total : data.length,
15372                     success : true
15373                     
15374                 }
15375         </pre>
15376             }.</li>
15377      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15378      * passed the following arguments:<ul>
15379      * <li>r : Roo.data.Record[]</li>
15380      * <li>options: Options object from the load call</li>
15381      * <li>success: Boolean success indicator</li></ul></li>
15382      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15383      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15384      * </ul>
15385      */
15386     load : function(options){
15387         options = options || {};
15388         if(this.fireEvent("beforeload", this, options) !== false){
15389             this.storeOptions(options);
15390             var p = Roo.apply(options.params || {}, this.baseParams);
15391             // if meta was not loaded from remote source.. try requesting it.
15392             if (!this.reader.metaFromRemote) {
15393                 p._requestMeta = 1;
15394             }
15395             if(this.sortInfo && this.remoteSort){
15396                 var pn = this.paramNames;
15397                 p[pn["sort"]] = this.sortInfo.field;
15398                 p[pn["dir"]] = this.sortInfo.direction;
15399             }
15400             if (this.multiSort) {
15401                 var pn = this.paramNames;
15402                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15403             }
15404             
15405             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15406         }
15407     },
15408
15409     /**
15410      * Reloads the Record cache from the configured Proxy using the configured Reader and
15411      * the options from the last load operation performed.
15412      * @param {Object} options (optional) An object containing properties which may override the options
15413      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15414      * the most recently used options are reused).
15415      */
15416     reload : function(options){
15417         this.load(Roo.applyIf(options||{}, this.lastOptions));
15418     },
15419
15420     // private
15421     // Called as a callback by the Reader during a load operation.
15422     loadRecords : function(o, options, success){
15423          
15424         if(!o){
15425             if(success !== false){
15426                 this.fireEvent("load", this, [], options, o);
15427             }
15428             if(options.callback){
15429                 options.callback.call(options.scope || this, [], options, false);
15430             }
15431             return;
15432         }
15433         // if data returned failure - throw an exception.
15434         if (o.success === false) {
15435             // show a message if no listener is registered.
15436             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15437                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15438             }
15439             // loadmask wil be hooked into this..
15440             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15441             return;
15442         }
15443         var r = o.records, t = o.totalRecords || r.length;
15444         
15445         this.fireEvent("beforeloadadd", this, r, options, o);
15446         
15447         if(!options || options.add !== true){
15448             if(this.pruneModifiedRecords){
15449                 this.modified = [];
15450             }
15451             for(var i = 0, len = r.length; i < len; i++){
15452                 r[i].join(this);
15453             }
15454             if(this.snapshot){
15455                 this.data = this.snapshot;
15456                 delete this.snapshot;
15457             }
15458             this.data.clear();
15459             this.data.addAll(r);
15460             this.totalLength = t;
15461             this.applySort();
15462             this.fireEvent("datachanged", this);
15463         }else{
15464             this.totalLength = Math.max(t, this.data.length+r.length);
15465             this.add(r);
15466         }
15467         
15468         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15469                 
15470             var e = new Roo.data.Record({});
15471
15472             e.set(this.parent.displayField, this.parent.emptyTitle);
15473             e.set(this.parent.valueField, '');
15474
15475             this.insert(0, e);
15476         }
15477             
15478         this.fireEvent("load", this, r, options, o);
15479         if(options.callback){
15480             options.callback.call(options.scope || this, r, options, true);
15481         }
15482     },
15483
15484
15485     /**
15486      * Loads data from a passed data block. A Reader which understands the format of the data
15487      * must have been configured in the constructor.
15488      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15489      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15490      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15491      */
15492     loadData : function(o, append){
15493         var r = this.reader.readRecords(o);
15494         this.loadRecords(r, {add: append}, true);
15495     },
15496     
15497      /**
15498      * using 'cn' the nested child reader read the child array into it's child stores.
15499      * @param {Object} rec The record with a 'children array
15500      */
15501     loadDataFromChildren : function(rec)
15502     {
15503         this.loadData(this.reader.toLoadData(rec));
15504     },
15505     
15506
15507     /**
15508      * Gets the number of cached records.
15509      * <p>
15510      * <em>If using paging, this may not be the total size of the dataset. If the data object
15511      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15512      * the data set size</em>
15513      */
15514     getCount : function(){
15515         return this.data.length || 0;
15516     },
15517
15518     /**
15519      * Gets the total number of records in the dataset as returned by the server.
15520      * <p>
15521      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15522      * the dataset size</em>
15523      */
15524     getTotalCount : function(){
15525         return this.totalLength || 0;
15526     },
15527
15528     /**
15529      * Returns the sort state of the Store as an object with two properties:
15530      * <pre><code>
15531  field {String} The name of the field by which the Records are sorted
15532  direction {String} The sort order, "ASC" or "DESC"
15533      * </code></pre>
15534      */
15535     getSortState : function(){
15536         return this.sortInfo;
15537     },
15538
15539     // private
15540     applySort : function(){
15541         if(this.sortInfo && !this.remoteSort){
15542             var s = this.sortInfo, f = s.field;
15543             var st = this.fields.get(f).sortType;
15544             var fn = function(r1, r2){
15545                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15546                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15547             };
15548             this.data.sort(s.direction, fn);
15549             if(this.snapshot && this.snapshot != this.data){
15550                 this.snapshot.sort(s.direction, fn);
15551             }
15552         }
15553     },
15554
15555     /**
15556      * Sets the default sort column and order to be used by the next load operation.
15557      * @param {String} fieldName The name of the field to sort by.
15558      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15559      */
15560     setDefaultSort : function(field, dir){
15561         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15562     },
15563
15564     /**
15565      * Sort the Records.
15566      * If remote sorting is used, the sort is performed on the server, and the cache is
15567      * reloaded. If local sorting is used, the cache is sorted internally.
15568      * @param {String} fieldName The name of the field to sort by.
15569      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15570      */
15571     sort : function(fieldName, dir){
15572         var f = this.fields.get(fieldName);
15573         if(!dir){
15574             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15575             
15576             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15577                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15578             }else{
15579                 dir = f.sortDir;
15580             }
15581         }
15582         this.sortToggle[f.name] = dir;
15583         this.sortInfo = {field: f.name, direction: dir};
15584         if(!this.remoteSort){
15585             this.applySort();
15586             this.fireEvent("datachanged", this);
15587         }else{
15588             this.load(this.lastOptions);
15589         }
15590     },
15591
15592     /**
15593      * Calls the specified function for each of the Records in the cache.
15594      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15595      * Returning <em>false</em> aborts and exits the iteration.
15596      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15597      */
15598     each : function(fn, scope){
15599         this.data.each(fn, scope);
15600     },
15601
15602     /**
15603      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15604      * (e.g., during paging).
15605      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15606      */
15607     getModifiedRecords : function(){
15608         return this.modified;
15609     },
15610
15611     // private
15612     createFilterFn : function(property, value, anyMatch){
15613         if(!value.exec){ // not a regex
15614             value = String(value);
15615             if(value.length == 0){
15616                 return false;
15617             }
15618             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15619         }
15620         return function(r){
15621             return value.test(r.data[property]);
15622         };
15623     },
15624
15625     /**
15626      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15627      * @param {String} property A field on your records
15628      * @param {Number} start The record index to start at (defaults to 0)
15629      * @param {Number} end The last record index to include (defaults to length - 1)
15630      * @return {Number} The sum
15631      */
15632     sum : function(property, start, end){
15633         var rs = this.data.items, v = 0;
15634         start = start || 0;
15635         end = (end || end === 0) ? end : rs.length-1;
15636
15637         for(var i = start; i <= end; i++){
15638             v += (rs[i].data[property] || 0);
15639         }
15640         return v;
15641     },
15642
15643     /**
15644      * Filter the records by a specified property.
15645      * @param {String} field A field on your records
15646      * @param {String/RegExp} value Either a string that the field
15647      * should start with or a RegExp to test against the field
15648      * @param {Boolean} anyMatch True to match any part not just the beginning
15649      */
15650     filter : function(property, value, anyMatch){
15651         var fn = this.createFilterFn(property, value, anyMatch);
15652         return fn ? this.filterBy(fn) : this.clearFilter();
15653     },
15654
15655     /**
15656      * Filter by a function. The specified function will be called with each
15657      * record in this data source. If the function returns true the record is included,
15658      * otherwise it is filtered.
15659      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15660      * @param {Object} scope (optional) The scope of the function (defaults to this)
15661      */
15662     filterBy : function(fn, scope){
15663         this.snapshot = this.snapshot || this.data;
15664         this.data = this.queryBy(fn, scope||this);
15665         this.fireEvent("datachanged", this);
15666     },
15667
15668     /**
15669      * Query the records by a specified property.
15670      * @param {String} field A field on your records
15671      * @param {String/RegExp} value Either a string that the field
15672      * should start with or a RegExp to test against the field
15673      * @param {Boolean} anyMatch True to match any part not just the beginning
15674      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15675      */
15676     query : function(property, value, anyMatch){
15677         var fn = this.createFilterFn(property, value, anyMatch);
15678         return fn ? this.queryBy(fn) : this.data.clone();
15679     },
15680
15681     /**
15682      * Query by a function. The specified function will be called with each
15683      * record in this data source. If the function returns true the record is included
15684      * in the results.
15685      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15686      * @param {Object} scope (optional) The scope of the function (defaults to this)
15687       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15688      **/
15689     queryBy : function(fn, scope){
15690         var data = this.snapshot || this.data;
15691         return data.filterBy(fn, scope||this);
15692     },
15693
15694     /**
15695      * Collects unique values for a particular dataIndex from this store.
15696      * @param {String} dataIndex The property to collect
15697      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15698      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15699      * @return {Array} An array of the unique values
15700      **/
15701     collect : function(dataIndex, allowNull, bypassFilter){
15702         var d = (bypassFilter === true && this.snapshot) ?
15703                 this.snapshot.items : this.data.items;
15704         var v, sv, r = [], l = {};
15705         for(var i = 0, len = d.length; i < len; i++){
15706             v = d[i].data[dataIndex];
15707             sv = String(v);
15708             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15709                 l[sv] = true;
15710                 r[r.length] = v;
15711             }
15712         }
15713         return r;
15714     },
15715
15716     /**
15717      * Revert to a view of the Record cache with no filtering applied.
15718      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15719      */
15720     clearFilter : function(suppressEvent){
15721         if(this.snapshot && this.snapshot != this.data){
15722             this.data = this.snapshot;
15723             delete this.snapshot;
15724             if(suppressEvent !== true){
15725                 this.fireEvent("datachanged", this);
15726             }
15727         }
15728     },
15729
15730     // private
15731     afterEdit : function(record){
15732         if(this.modified.indexOf(record) == -1){
15733             this.modified.push(record);
15734         }
15735         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15736     },
15737     
15738     // private
15739     afterReject : function(record){
15740         this.modified.remove(record);
15741         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15742     },
15743
15744     // private
15745     afterCommit : function(record){
15746         this.modified.remove(record);
15747         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15748     },
15749
15750     /**
15751      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15752      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15753      */
15754     commitChanges : function(){
15755         var m = this.modified.slice(0);
15756         this.modified = [];
15757         for(var i = 0, len = m.length; i < len; i++){
15758             m[i].commit();
15759         }
15760     },
15761
15762     /**
15763      * Cancel outstanding changes on all changed records.
15764      */
15765     rejectChanges : function(){
15766         var m = this.modified.slice(0);
15767         this.modified = [];
15768         for(var i = 0, len = m.length; i < len; i++){
15769             m[i].reject();
15770         }
15771     },
15772
15773     onMetaChange : function(meta, rtype, o){
15774         this.recordType = rtype;
15775         this.fields = rtype.prototype.fields;
15776         delete this.snapshot;
15777         this.sortInfo = meta.sortInfo || this.sortInfo;
15778         this.modified = [];
15779         this.fireEvent('metachange', this, this.reader.meta);
15780     },
15781     
15782     moveIndex : function(data, type)
15783     {
15784         var index = this.indexOf(data);
15785         
15786         var newIndex = index + type;
15787         
15788         this.remove(data);
15789         
15790         this.insert(newIndex, data);
15791         
15792     }
15793 });/*
15794  * Based on:
15795  * Ext JS Library 1.1.1
15796  * Copyright(c) 2006-2007, Ext JS, LLC.
15797  *
15798  * Originally Released Under LGPL - original licence link has changed is not relivant.
15799  *
15800  * Fork - LGPL
15801  * <script type="text/javascript">
15802  */
15803
15804 /**
15805  * @class Roo.data.SimpleStore
15806  * @extends Roo.data.Store
15807  * Small helper class to make creating Stores from Array data easier.
15808  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15809  * @cfg {Array} fields An array of field definition objects, or field name strings.
15810  * @cfg {Object} an existing reader (eg. copied from another store)
15811  * @cfg {Array} data The multi-dimensional array of data
15812  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15813  * @cfg {Roo.data.Reader} reader  [not-required] 
15814  * @constructor
15815  * @param {Object} config
15816  */
15817 Roo.data.SimpleStore = function(config)
15818 {
15819     Roo.data.SimpleStore.superclass.constructor.call(this, {
15820         isLocal : true,
15821         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15822                 id: config.id
15823             },
15824             Roo.data.Record.create(config.fields)
15825         ),
15826         proxy : new Roo.data.MemoryProxy(config.data)
15827     });
15828     this.load();
15829 };
15830 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15831  * Based on:
15832  * Ext JS Library 1.1.1
15833  * Copyright(c) 2006-2007, Ext JS, LLC.
15834  *
15835  * Originally Released Under LGPL - original licence link has changed is not relivant.
15836  *
15837  * Fork - LGPL
15838  * <script type="text/javascript">
15839  */
15840
15841 /**
15842 /**
15843  * @extends Roo.data.Store
15844  * @class Roo.data.JsonStore
15845  * Small helper class to make creating Stores for JSON data easier. <br/>
15846 <pre><code>
15847 var store = new Roo.data.JsonStore({
15848     url: 'get-images.php',
15849     root: 'images',
15850     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15851 });
15852 </code></pre>
15853  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15854  * JsonReader and HttpProxy (unless inline data is provided).</b>
15855  * @cfg {Array} fields An array of field definition objects, or field name strings.
15856  * @constructor
15857  * @param {Object} config
15858  */
15859 Roo.data.JsonStore = function(c){
15860     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15861         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15862         reader: new Roo.data.JsonReader(c, c.fields)
15863     }));
15864 };
15865 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15866  * Based on:
15867  * Ext JS Library 1.1.1
15868  * Copyright(c) 2006-2007, Ext JS, LLC.
15869  *
15870  * Originally Released Under LGPL - original licence link has changed is not relivant.
15871  *
15872  * Fork - LGPL
15873  * <script type="text/javascript">
15874  */
15875
15876  
15877 Roo.data.Field = function(config){
15878     if(typeof config == "string"){
15879         config = {name: config};
15880     }
15881     Roo.apply(this, config);
15882     
15883     if(!this.type){
15884         this.type = "auto";
15885     }
15886     
15887     var st = Roo.data.SortTypes;
15888     // named sortTypes are supported, here we look them up
15889     if(typeof this.sortType == "string"){
15890         this.sortType = st[this.sortType];
15891     }
15892     
15893     // set default sortType for strings and dates
15894     if(!this.sortType){
15895         switch(this.type){
15896             case "string":
15897                 this.sortType = st.asUCString;
15898                 break;
15899             case "date":
15900                 this.sortType = st.asDate;
15901                 break;
15902             default:
15903                 this.sortType = st.none;
15904         }
15905     }
15906
15907     // define once
15908     var stripRe = /[\$,%]/g;
15909
15910     // prebuilt conversion function for this field, instead of
15911     // switching every time we're reading a value
15912     if(!this.convert){
15913         var cv, dateFormat = this.dateFormat;
15914         switch(this.type){
15915             case "":
15916             case "auto":
15917             case undefined:
15918                 cv = function(v){ return v; };
15919                 break;
15920             case "string":
15921                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15922                 break;
15923             case "int":
15924                 cv = function(v){
15925                     return v !== undefined && v !== null && v !== '' ?
15926                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15927                     };
15928                 break;
15929             case "float":
15930                 cv = function(v){
15931                     return v !== undefined && v !== null && v !== '' ?
15932                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15933                     };
15934                 break;
15935             case "bool":
15936             case "boolean":
15937                 cv = function(v){ return v === true || v === "true" || v == 1; };
15938                 break;
15939             case "date":
15940                 cv = function(v){
15941                     if(!v){
15942                         return '';
15943                     }
15944                     if(v instanceof Date){
15945                         return v;
15946                     }
15947                     if(dateFormat){
15948                         if(dateFormat == "timestamp"){
15949                             return new Date(v*1000);
15950                         }
15951                         return Date.parseDate(v, dateFormat);
15952                     }
15953                     var parsed = Date.parse(v);
15954                     return parsed ? new Date(parsed) : null;
15955                 };
15956              break;
15957             
15958         }
15959         this.convert = cv;
15960     }
15961 };
15962
15963 Roo.data.Field.prototype = {
15964     dateFormat: null,
15965     defaultValue: "",
15966     mapping: null,
15967     sortType : null,
15968     sortDir : "ASC"
15969 };/*
15970  * Based on:
15971  * Ext JS Library 1.1.1
15972  * Copyright(c) 2006-2007, Ext JS, LLC.
15973  *
15974  * Originally Released Under LGPL - original licence link has changed is not relivant.
15975  *
15976  * Fork - LGPL
15977  * <script type="text/javascript">
15978  */
15979  
15980 // Base class for reading structured data from a data source.  This class is intended to be
15981 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15982
15983 /**
15984  * @class Roo.data.DataReader
15985  * @abstract
15986  * Base class for reading structured data from a data source.  This class is intended to be
15987  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15988  */
15989
15990 Roo.data.DataReader = function(meta, recordType){
15991     
15992     this.meta = meta;
15993     
15994     this.recordType = recordType instanceof Array ? 
15995         Roo.data.Record.create(recordType) : recordType;
15996 };
15997
15998 Roo.data.DataReader.prototype = {
15999     
16000     
16001     readerType : 'Data',
16002      /**
16003      * Create an empty record
16004      * @param {Object} data (optional) - overlay some values
16005      * @return {Roo.data.Record} record created.
16006      */
16007     newRow :  function(d) {
16008         var da =  {};
16009         this.recordType.prototype.fields.each(function(c) {
16010             switch( c.type) {
16011                 case 'int' : da[c.name] = 0; break;
16012                 case 'date' : da[c.name] = new Date(); break;
16013                 case 'float' : da[c.name] = 0.0; break;
16014                 case 'boolean' : da[c.name] = false; break;
16015                 default : da[c.name] = ""; break;
16016             }
16017             
16018         });
16019         return new this.recordType(Roo.apply(da, d));
16020     }
16021     
16022     
16023 };/*
16024  * Based on:
16025  * Ext JS Library 1.1.1
16026  * Copyright(c) 2006-2007, Ext JS, LLC.
16027  *
16028  * Originally Released Under LGPL - original licence link has changed is not relivant.
16029  *
16030  * Fork - LGPL
16031  * <script type="text/javascript">
16032  */
16033
16034 /**
16035  * @class Roo.data.DataProxy
16036  * @extends Roo.util.Observable
16037  * @abstract
16038  * This class is an abstract base class for implementations which provide retrieval of
16039  * unformatted data objects.<br>
16040  * <p>
16041  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16042  * (of the appropriate type which knows how to parse the data object) to provide a block of
16043  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16044  * <p>
16045  * Custom implementations must implement the load method as described in
16046  * {@link Roo.data.HttpProxy#load}.
16047  */
16048 Roo.data.DataProxy = function(){
16049     this.addEvents({
16050         /**
16051          * @event beforeload
16052          * Fires before a network request is made to retrieve a data object.
16053          * @param {Object} This DataProxy object.
16054          * @param {Object} params The params parameter to the load function.
16055          */
16056         beforeload : true,
16057         /**
16058          * @event load
16059          * Fires before the load method's callback is called.
16060          * @param {Object} This DataProxy object.
16061          * @param {Object} o The data object.
16062          * @param {Object} arg The callback argument object passed to the load function.
16063          */
16064         load : true,
16065         /**
16066          * @event loadexception
16067          * Fires if an Exception occurs during data retrieval.
16068          * @param {Object} This DataProxy object.
16069          * @param {Object} o The data object.
16070          * @param {Object} arg The callback argument object passed to the load function.
16071          * @param {Object} e The Exception.
16072          */
16073         loadexception : true
16074     });
16075     Roo.data.DataProxy.superclass.constructor.call(this);
16076 };
16077
16078 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16079
16080     /**
16081      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16082      */
16083 /*
16084  * Based on:
16085  * Ext JS Library 1.1.1
16086  * Copyright(c) 2006-2007, Ext JS, LLC.
16087  *
16088  * Originally Released Under LGPL - original licence link has changed is not relivant.
16089  *
16090  * Fork - LGPL
16091  * <script type="text/javascript">
16092  */
16093 /**
16094  * @class Roo.data.MemoryProxy
16095  * @extends Roo.data.DataProxy
16096  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16097  * to the Reader when its load method is called.
16098  * @constructor
16099  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16100  */
16101 Roo.data.MemoryProxy = function(config){
16102     var data = config;
16103     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16104         data = config.data;
16105     }
16106     Roo.data.MemoryProxy.superclass.constructor.call(this);
16107     this.data = data;
16108 };
16109
16110 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16111     
16112     /**
16113      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16114      */
16115     /**
16116      * Load data from the requested source (in this case an in-memory
16117      * data object passed to the constructor), read the data object into
16118      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16119      * process that block using the passed callback.
16120      * @param {Object} params This parameter is not used by the MemoryProxy class.
16121      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16122      * object into a block of Roo.data.Records.
16123      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16124      * The function must be passed <ul>
16125      * <li>The Record block object</li>
16126      * <li>The "arg" argument from the load function</li>
16127      * <li>A boolean success indicator</li>
16128      * </ul>
16129      * @param {Object} scope The scope in which to call the callback
16130      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16131      */
16132     load : function(params, reader, callback, scope, arg){
16133         params = params || {};
16134         var result;
16135         try {
16136             result = reader.readRecords(params.data ? params.data :this.data);
16137         }catch(e){
16138             this.fireEvent("loadexception", this, arg, null, e);
16139             callback.call(scope, null, arg, false);
16140             return;
16141         }
16142         callback.call(scope, result, arg, true);
16143     },
16144     
16145     // private
16146     update : function(params, records){
16147         
16148     }
16149 });/*
16150  * Based on:
16151  * Ext JS Library 1.1.1
16152  * Copyright(c) 2006-2007, Ext JS, LLC.
16153  *
16154  * Originally Released Under LGPL - original licence link has changed is not relivant.
16155  *
16156  * Fork - LGPL
16157  * <script type="text/javascript">
16158  */
16159 /**
16160  * @class Roo.data.HttpProxy
16161  * @extends Roo.data.DataProxy
16162  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16163  * configured to reference a certain URL.<br><br>
16164  * <p>
16165  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16166  * from which the running page was served.<br><br>
16167  * <p>
16168  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16169  * <p>
16170  * Be aware that to enable the browser to parse an XML document, the server must set
16171  * the Content-Type header in the HTTP response to "text/xml".
16172  * @constructor
16173  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16174  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16175  * will be used to make the request.
16176  */
16177 Roo.data.HttpProxy = function(conn){
16178     Roo.data.HttpProxy.superclass.constructor.call(this);
16179     // is conn a conn config or a real conn?
16180     this.conn = conn;
16181     this.useAjax = !conn || !conn.events;
16182   
16183 };
16184
16185 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16186     // thse are take from connection...
16187     
16188     /**
16189      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16190      */
16191     /**
16192      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16193      * extra parameters to each request made by this object. (defaults to undefined)
16194      */
16195     /**
16196      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16197      *  to each request made by this object. (defaults to undefined)
16198      */
16199     /**
16200      * @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)
16201      */
16202     /**
16203      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16204      */
16205      /**
16206      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16207      * @type Boolean
16208      */
16209   
16210
16211     /**
16212      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16213      * @type Boolean
16214      */
16215     /**
16216      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16217      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16218      * a finer-grained basis than the DataProxy events.
16219      */
16220     getConnection : function(){
16221         return this.useAjax ? Roo.Ajax : this.conn;
16222     },
16223
16224     /**
16225      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16226      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16227      * process that block using the passed callback.
16228      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16229      * for the request to the remote server.
16230      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16231      * object into a block of Roo.data.Records.
16232      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16233      * The function must be passed <ul>
16234      * <li>The Record block object</li>
16235      * <li>The "arg" argument from the load function</li>
16236      * <li>A boolean success indicator</li>
16237      * </ul>
16238      * @param {Object} scope The scope in which to call the callback
16239      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16240      */
16241     load : function(params, reader, callback, scope, arg){
16242         if(this.fireEvent("beforeload", this, params) !== false){
16243             var  o = {
16244                 params : params || {},
16245                 request: {
16246                     callback : callback,
16247                     scope : scope,
16248                     arg : arg
16249                 },
16250                 reader: reader,
16251                 callback : this.loadResponse,
16252                 scope: this
16253             };
16254             if(this.useAjax){
16255                 Roo.applyIf(o, this.conn);
16256                 if(this.activeRequest){
16257                     Roo.Ajax.abort(this.activeRequest);
16258                 }
16259                 this.activeRequest = Roo.Ajax.request(o);
16260             }else{
16261                 this.conn.request(o);
16262             }
16263         }else{
16264             callback.call(scope||this, null, arg, false);
16265         }
16266     },
16267
16268     // private
16269     loadResponse : function(o, success, response){
16270         delete this.activeRequest;
16271         if(!success){
16272             this.fireEvent("loadexception", this, o, response);
16273             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16274             return;
16275         }
16276         var result;
16277         try {
16278             result = o.reader.read(response);
16279         }catch(e){
16280             o.success = false;
16281             o.raw = { errorMsg : response.responseText };
16282             this.fireEvent("loadexception", this, o, response, e);
16283             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16284             return;
16285         }
16286         
16287         this.fireEvent("load", this, o, o.request.arg);
16288         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16289     },
16290
16291     // private
16292     update : function(dataSet){
16293
16294     },
16295
16296     // private
16297     updateResponse : function(dataSet){
16298
16299     }
16300 });/*
16301  * Based on:
16302  * Ext JS Library 1.1.1
16303  * Copyright(c) 2006-2007, Ext JS, LLC.
16304  *
16305  * Originally Released Under LGPL - original licence link has changed is not relivant.
16306  *
16307  * Fork - LGPL
16308  * <script type="text/javascript">
16309  */
16310
16311 /**
16312  * @class Roo.data.ScriptTagProxy
16313  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16314  * other than the originating domain of the running page.<br><br>
16315  * <p>
16316  * <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
16317  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16318  * <p>
16319  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16320  * source code that is used as the source inside a &lt;script> tag.<br><br>
16321  * <p>
16322  * In order for the browser to process the returned data, the server must wrap the data object
16323  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16324  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16325  * depending on whether the callback name was passed:
16326  * <p>
16327  * <pre><code>
16328 boolean scriptTag = false;
16329 String cb = request.getParameter("callback");
16330 if (cb != null) {
16331     scriptTag = true;
16332     response.setContentType("text/javascript");
16333 } else {
16334     response.setContentType("application/x-json");
16335 }
16336 Writer out = response.getWriter();
16337 if (scriptTag) {
16338     out.write(cb + "(");
16339 }
16340 out.print(dataBlock.toJsonString());
16341 if (scriptTag) {
16342     out.write(");");
16343 }
16344 </pre></code>
16345  *
16346  * @constructor
16347  * @param {Object} config A configuration object.
16348  */
16349 Roo.data.ScriptTagProxy = function(config){
16350     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16351     Roo.apply(this, config);
16352     this.head = document.getElementsByTagName("head")[0];
16353 };
16354
16355 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16356
16357 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16358     /**
16359      * @cfg {String} url The URL from which to request the data object.
16360      */
16361     /**
16362      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16363      */
16364     timeout : 30000,
16365     /**
16366      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16367      * the server the name of the callback function set up by the load call to process the returned data object.
16368      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16369      * javascript output which calls this named function passing the data object as its only parameter.
16370      */
16371     callbackParam : "callback",
16372     /**
16373      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16374      * name to the request.
16375      */
16376     nocache : true,
16377
16378     /**
16379      * Load data from the configured URL, read the data object into
16380      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16381      * process that block using the passed callback.
16382      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16383      * for the request to the remote server.
16384      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16385      * object into a block of Roo.data.Records.
16386      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16387      * The function must be passed <ul>
16388      * <li>The Record block object</li>
16389      * <li>The "arg" argument from the load function</li>
16390      * <li>A boolean success indicator</li>
16391      * </ul>
16392      * @param {Object} scope The scope in which to call the callback
16393      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16394      */
16395     load : function(params, reader, callback, scope, arg){
16396         if(this.fireEvent("beforeload", this, params) !== false){
16397
16398             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16399
16400             var url = this.url;
16401             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16402             if(this.nocache){
16403                 url += "&_dc=" + (new Date().getTime());
16404             }
16405             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16406             var trans = {
16407                 id : transId,
16408                 cb : "stcCallback"+transId,
16409                 scriptId : "stcScript"+transId,
16410                 params : params,
16411                 arg : arg,
16412                 url : url,
16413                 callback : callback,
16414                 scope : scope,
16415                 reader : reader
16416             };
16417             var conn = this;
16418
16419             window[trans.cb] = function(o){
16420                 conn.handleResponse(o, trans);
16421             };
16422
16423             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16424
16425             if(this.autoAbort !== false){
16426                 this.abort();
16427             }
16428
16429             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16430
16431             var script = document.createElement("script");
16432             script.setAttribute("src", url);
16433             script.setAttribute("type", "text/javascript");
16434             script.setAttribute("id", trans.scriptId);
16435             this.head.appendChild(script);
16436
16437             this.trans = trans;
16438         }else{
16439             callback.call(scope||this, null, arg, false);
16440         }
16441     },
16442
16443     // private
16444     isLoading : function(){
16445         return this.trans ? true : false;
16446     },
16447
16448     /**
16449      * Abort the current server request.
16450      */
16451     abort : function(){
16452         if(this.isLoading()){
16453             this.destroyTrans(this.trans);
16454         }
16455     },
16456
16457     // private
16458     destroyTrans : function(trans, isLoaded){
16459         this.head.removeChild(document.getElementById(trans.scriptId));
16460         clearTimeout(trans.timeoutId);
16461         if(isLoaded){
16462             window[trans.cb] = undefined;
16463             try{
16464                 delete window[trans.cb];
16465             }catch(e){}
16466         }else{
16467             // if hasn't been loaded, wait for load to remove it to prevent script error
16468             window[trans.cb] = function(){
16469                 window[trans.cb] = undefined;
16470                 try{
16471                     delete window[trans.cb];
16472                 }catch(e){}
16473             };
16474         }
16475     },
16476
16477     // private
16478     handleResponse : function(o, trans){
16479         this.trans = false;
16480         this.destroyTrans(trans, true);
16481         var result;
16482         try {
16483             result = trans.reader.readRecords(o);
16484         }catch(e){
16485             this.fireEvent("loadexception", this, o, trans.arg, e);
16486             trans.callback.call(trans.scope||window, null, trans.arg, false);
16487             return;
16488         }
16489         this.fireEvent("load", this, o, trans.arg);
16490         trans.callback.call(trans.scope||window, result, trans.arg, true);
16491     },
16492
16493     // private
16494     handleFailure : function(trans){
16495         this.trans = false;
16496         this.destroyTrans(trans, false);
16497         this.fireEvent("loadexception", this, null, trans.arg);
16498         trans.callback.call(trans.scope||window, null, trans.arg, false);
16499     }
16500 });/*
16501  * Based on:
16502  * Ext JS Library 1.1.1
16503  * Copyright(c) 2006-2007, Ext JS, LLC.
16504  *
16505  * Originally Released Under LGPL - original licence link has changed is not relivant.
16506  *
16507  * Fork - LGPL
16508  * <script type="text/javascript">
16509  */
16510
16511 /**
16512  * @class Roo.data.JsonReader
16513  * @extends Roo.data.DataReader
16514  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16515  * based on mappings in a provided Roo.data.Record constructor.
16516  * 
16517  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16518  * in the reply previously. 
16519  * 
16520  * <p>
16521  * Example code:
16522  * <pre><code>
16523 var RecordDef = Roo.data.Record.create([
16524     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16525     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16526 ]);
16527 var myReader = new Roo.data.JsonReader({
16528     totalProperty: "results",    // The property which contains the total dataset size (optional)
16529     root: "rows",                // The property which contains an Array of row objects
16530     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16531 }, RecordDef);
16532 </code></pre>
16533  * <p>
16534  * This would consume a JSON file like this:
16535  * <pre><code>
16536 { 'results': 2, 'rows': [
16537     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16538     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16539 }
16540 </code></pre>
16541  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16542  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16543  * paged from the remote server.
16544  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16545  * @cfg {String} root name of the property which contains the Array of row objects.
16546  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16547  * @cfg {Array} fields Array of field definition objects
16548  * @constructor
16549  * Create a new JsonReader
16550  * @param {Object} meta Metadata configuration options
16551  * @param {Object} recordType Either an Array of field definition objects,
16552  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16553  */
16554 Roo.data.JsonReader = function(meta, recordType){
16555     
16556     meta = meta || {};
16557     // set some defaults:
16558     Roo.applyIf(meta, {
16559         totalProperty: 'total',
16560         successProperty : 'success',
16561         root : 'data',
16562         id : 'id'
16563     });
16564     
16565     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16566 };
16567 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16568     
16569     readerType : 'Json',
16570     
16571     /**
16572      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16573      * Used by Store query builder to append _requestMeta to params.
16574      * 
16575      */
16576     metaFromRemote : false,
16577     /**
16578      * This method is only used by a DataProxy which has retrieved data from a remote server.
16579      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16580      * @return {Object} data A data block which is used by an Roo.data.Store object as
16581      * a cache of Roo.data.Records.
16582      */
16583     read : function(response){
16584         var json = response.responseText;
16585        
16586         var o = /* eval:var:o */ eval("("+json+")");
16587         if(!o) {
16588             throw {message: "JsonReader.read: Json object not found"};
16589         }
16590         
16591         if(o.metaData){
16592             
16593             delete this.ef;
16594             this.metaFromRemote = true;
16595             this.meta = o.metaData;
16596             this.recordType = Roo.data.Record.create(o.metaData.fields);
16597             this.onMetaChange(this.meta, this.recordType, o);
16598         }
16599         return this.readRecords(o);
16600     },
16601
16602     // private function a store will implement
16603     onMetaChange : function(meta, recordType, o){
16604
16605     },
16606
16607     /**
16608          * @ignore
16609          */
16610     simpleAccess: function(obj, subsc) {
16611         return obj[subsc];
16612     },
16613
16614         /**
16615          * @ignore
16616          */
16617     getJsonAccessor: function(){
16618         var re = /[\[\.]/;
16619         return function(expr) {
16620             try {
16621                 return(re.test(expr))
16622                     ? new Function("obj", "return obj." + expr)
16623                     : function(obj){
16624                         return obj[expr];
16625                     };
16626             } catch(e){}
16627             return Roo.emptyFn;
16628         };
16629     }(),
16630
16631     /**
16632      * Create a data block containing Roo.data.Records from an XML document.
16633      * @param {Object} o An object which contains an Array of row objects in the property specified
16634      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16635      * which contains the total size of the dataset.
16636      * @return {Object} data A data block which is used by an Roo.data.Store object as
16637      * a cache of Roo.data.Records.
16638      */
16639     readRecords : function(o){
16640         /**
16641          * After any data loads, the raw JSON data is available for further custom processing.
16642          * @type Object
16643          */
16644         this.o = o;
16645         var s = this.meta, Record = this.recordType,
16646             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16647
16648 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16649         if (!this.ef) {
16650             if(s.totalProperty) {
16651                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16652                 }
16653                 if(s.successProperty) {
16654                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16655                 }
16656                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16657                 if (s.id) {
16658                         var g = this.getJsonAccessor(s.id);
16659                         this.getId = function(rec) {
16660                                 var r = g(rec);  
16661                                 return (r === undefined || r === "") ? null : r;
16662                         };
16663                 } else {
16664                         this.getId = function(){return null;};
16665                 }
16666             this.ef = [];
16667             for(var jj = 0; jj < fl; jj++){
16668                 f = fi[jj];
16669                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16670                 this.ef[jj] = this.getJsonAccessor(map);
16671             }
16672         }
16673
16674         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16675         if(s.totalProperty){
16676             var vt = parseInt(this.getTotal(o), 10);
16677             if(!isNaN(vt)){
16678                 totalRecords = vt;
16679             }
16680         }
16681         if(s.successProperty){
16682             var vs = this.getSuccess(o);
16683             if(vs === false || vs === 'false'){
16684                 success = false;
16685             }
16686         }
16687         var records = [];
16688         for(var i = 0; i < c; i++){
16689             var n = root[i];
16690             var values = {};
16691             var id = this.getId(n);
16692             for(var j = 0; j < fl; j++){
16693                 f = fi[j];
16694                                 var v = this.ef[j](n);
16695                                 if (!f.convert) {
16696                                         Roo.log('missing convert for ' + f.name);
16697                                         Roo.log(f);
16698                                         continue;
16699                                 }
16700                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16701             }
16702                         if (!Record) {
16703                                 return {
16704                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16705                                         success : false,
16706                                         records : [],
16707                                         totalRecords : 0
16708                                 };
16709                         }
16710             var record = new Record(values, id);
16711             record.json = n;
16712             records[i] = record;
16713         }
16714         return {
16715             raw : o,
16716             success : success,
16717             records : records,
16718             totalRecords : totalRecords
16719         };
16720     },
16721     // used when loading children.. @see loadDataFromChildren
16722     toLoadData: function(rec)
16723     {
16724         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16725         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16726         return { data : data, total : data.length };
16727         
16728     }
16729 });/*
16730  * Based on:
16731  * Ext JS Library 1.1.1
16732  * Copyright(c) 2006-2007, Ext JS, LLC.
16733  *
16734  * Originally Released Under LGPL - original licence link has changed is not relivant.
16735  *
16736  * Fork - LGPL
16737  * <script type="text/javascript">
16738  */
16739
16740 /**
16741  * @class Roo.data.ArrayReader
16742  * @extends Roo.data.DataReader
16743  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16744  * Each element of that Array represents a row of data fields. The
16745  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16746  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16747  * <p>
16748  * Example code:.
16749  * <pre><code>
16750 var RecordDef = Roo.data.Record.create([
16751     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16752     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16753 ]);
16754 var myReader = new Roo.data.ArrayReader({
16755     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16756 }, RecordDef);
16757 </code></pre>
16758  * <p>
16759  * This would consume an Array like this:
16760  * <pre><code>
16761 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16762   </code></pre>
16763  
16764  * @constructor
16765  * Create a new JsonReader
16766  * @param {Object} meta Metadata configuration options.
16767  * @param {Object|Array} recordType Either an Array of field definition objects
16768  * 
16769  * @cfg {Array} fields Array of field definition objects
16770  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16771  * as specified to {@link Roo.data.Record#create},
16772  * or an {@link Roo.data.Record} object
16773  *
16774  * 
16775  * created using {@link Roo.data.Record#create}.
16776  */
16777 Roo.data.ArrayReader = function(meta, recordType)
16778 {    
16779     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16780 };
16781
16782 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16783     
16784       /**
16785      * Create a data block containing Roo.data.Records from an XML document.
16786      * @param {Object} o An Array of row objects which represents the dataset.
16787      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16788      * a cache of Roo.data.Records.
16789      */
16790     readRecords : function(o)
16791     {
16792         var sid = this.meta ? this.meta.id : null;
16793         var recordType = this.recordType, fields = recordType.prototype.fields;
16794         var records = [];
16795         var root = o;
16796         for(var i = 0; i < root.length; i++){
16797             var n = root[i];
16798             var values = {};
16799             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16800             for(var j = 0, jlen = fields.length; j < jlen; j++){
16801                 var f = fields.items[j];
16802                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16803                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16804                 v = f.convert(v);
16805                 values[f.name] = v;
16806             }
16807             var record = new recordType(values, id);
16808             record.json = n;
16809             records[records.length] = record;
16810         }
16811         return {
16812             records : records,
16813             totalRecords : records.length
16814         };
16815     },
16816     // used when loading children.. @see loadDataFromChildren
16817     toLoadData: function(rec)
16818     {
16819         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16820         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16821         
16822     }
16823     
16824     
16825 });/*
16826  * - LGPL
16827  * * 
16828  */
16829
16830 /**
16831  * @class Roo.bootstrap.form.ComboBox
16832  * @extends Roo.bootstrap.form.TriggerField
16833  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16834  * @cfg {Boolean} append (true|false) default false
16835  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16836  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16837  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16838  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16839  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16840  * @cfg {Boolean} animate default true
16841  * @cfg {Boolean} emptyResultText only for touch device
16842  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16843  * @cfg {String} emptyTitle default ''
16844  * @cfg {Number} width fixed with? experimental
16845  * @constructor
16846  * Create a new ComboBox.
16847  * @param {Object} config Configuration options
16848  */
16849 Roo.bootstrap.form.ComboBox = function(config){
16850     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16851     this.addEvents({
16852         /**
16853          * @event expand
16854          * Fires when the dropdown list is expanded
16855         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16856         */
16857         'expand' : true,
16858         /**
16859          * @event collapse
16860          * Fires when the dropdown list is collapsed
16861         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16862         */
16863         'collapse' : true,
16864         /**
16865          * @event beforeselect
16866          * Fires before a list item is selected. Return false to cancel the selection.
16867         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16868         * @param {Roo.data.Record} record The data record returned from the underlying store
16869         * @param {Number} index The index of the selected item in the dropdown list
16870         */
16871         'beforeselect' : true,
16872         /**
16873          * @event select
16874          * Fires when a list item is selected
16875         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16877         * @param {Number} index The index of the selected item in the dropdown list
16878         */
16879         'select' : true,
16880         /**
16881          * @event beforequery
16882          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16883          * The event object passed has these properties:
16884         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16885         * @param {String} query The query
16886         * @param {Boolean} forceAll true to force "all" query
16887         * @param {Boolean} cancel true to cancel the query
16888         * @param {Object} e The query event object
16889         */
16890         'beforequery': true,
16891          /**
16892          * @event add
16893          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16894         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16895         */
16896         'add' : true,
16897         /**
16898          * @event edit
16899          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16900         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16902         */
16903         'edit' : true,
16904         /**
16905          * @event remove
16906          * Fires when the remove value from the combobox array
16907         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908         */
16909         'remove' : true,
16910         /**
16911          * @event afterremove
16912          * Fires when the remove value from the combobox array
16913         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914         */
16915         'afterremove' : true,
16916         /**
16917          * @event specialfilter
16918          * Fires when specialfilter
16919             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16920             */
16921         'specialfilter' : true,
16922         /**
16923          * @event tick
16924          * Fires when tick the element
16925             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16926             */
16927         'tick' : true,
16928         /**
16929          * @event touchviewdisplay
16930          * Fires when touch view require special display (default is using displayField)
16931             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16932             * @param {Object} cfg set html .
16933             */
16934         'touchviewdisplay' : true
16935         
16936     });
16937     
16938     this.item = [];
16939     this.tickItems = [];
16940     
16941     this.selectedIndex = -1;
16942     if(this.mode == 'local'){
16943         if(config.queryDelay === undefined){
16944             this.queryDelay = 10;
16945         }
16946         if(config.minChars === undefined){
16947             this.minChars = 0;
16948         }
16949     }
16950 };
16951
16952 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16953      
16954     /**
16955      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16956      * rendering into an Roo.Editor, defaults to false)
16957      */
16958     /**
16959      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16960      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16961      */
16962     /**
16963      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16964      */
16965     /**
16966      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16967      * the dropdown list (defaults to undefined, with no header element)
16968      */
16969
16970      /**
16971      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16972      */
16973      
16974      /**
16975      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16976      */
16977     listWidth: undefined,
16978     /**
16979      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16980      * mode = 'remote' or 'text' if mode = 'local')
16981      */
16982     displayField: undefined,
16983     
16984     /**
16985      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16986      * mode = 'remote' or 'value' if mode = 'local'). 
16987      * Note: use of a valueField requires the user make a selection
16988      * in order for a value to be mapped.
16989      */
16990     valueField: undefined,
16991     /**
16992      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16993      */
16994     modalTitle : '',
16995     
16996     /**
16997      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16998      * field's data value (defaults to the underlying DOM element's name)
16999      */
17000     hiddenName: undefined,
17001     /**
17002      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17003      */
17004     listClass: '',
17005     /**
17006      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17007      */
17008     selectedClass: 'active',
17009     
17010     /**
17011      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17012      */
17013     shadow:'sides',
17014     /**
17015      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17016      * anchor positions (defaults to 'tl-bl')
17017      */
17018     listAlign: 'tl-bl?',
17019     /**
17020      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17021      */
17022     maxHeight: 300,
17023     /**
17024      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17025      * query specified by the allQuery config option (defaults to 'query')
17026      */
17027     triggerAction: 'query',
17028     /**
17029      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17030      * (defaults to 4, does not apply if editable = false)
17031      */
17032     minChars : 4,
17033     /**
17034      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17035      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17036      */
17037     typeAhead: false,
17038     /**
17039      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17040      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17041      */
17042     queryDelay: 500,
17043     /**
17044      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17045      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17046      */
17047     pageSize: 0,
17048     /**
17049      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17050      * when editable = true (defaults to false)
17051      */
17052     selectOnFocus:false,
17053     /**
17054      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17055      */
17056     queryParam: 'query',
17057     /**
17058      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17059      * when mode = 'remote' (defaults to 'Loading...')
17060      */
17061     loadingText: 'Loading...',
17062     /**
17063      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17064      */
17065     resizable: false,
17066     /**
17067      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17068      */
17069     handleHeight : 8,
17070     /**
17071      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17072      * traditional select (defaults to true)
17073      */
17074     editable: true,
17075     /**
17076      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17077      */
17078     allQuery: '',
17079     /**
17080      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17081      */
17082     mode: 'remote',
17083     /**
17084      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17085      * listWidth has a higher value)
17086      */
17087     minListWidth : 70,
17088     /**
17089      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17090      * allow the user to set arbitrary text into the field (defaults to false)
17091      */
17092     forceSelection:false,
17093     /**
17094      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17095      * if typeAhead = true (defaults to 250)
17096      */
17097     typeAheadDelay : 250,
17098     /**
17099      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17100      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17101      */
17102     valueNotFoundText : undefined,
17103     /**
17104      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17105      */
17106     blockFocus : false,
17107     
17108     /**
17109      * @cfg {Boolean} disableClear Disable showing of clear button.
17110      */
17111     disableClear : false,
17112     /**
17113      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17114      */
17115     alwaysQuery : false,
17116     
17117     /**
17118      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17119      */
17120     multiple : false,
17121     
17122     /**
17123      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17124      */
17125     invalidClass : "has-warning",
17126     
17127     /**
17128      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17129      */
17130     validClass : "has-success",
17131     
17132     /**
17133      * @cfg {Boolean} specialFilter (true|false) special filter default false
17134      */
17135     specialFilter : false,
17136     
17137     /**
17138      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17139      */
17140     mobileTouchView : true,
17141     
17142     /**
17143      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17144      */
17145     useNativeIOS : false,
17146     
17147     /**
17148      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17149      */
17150     mobile_restrict_height : false,
17151     
17152     ios_options : false,
17153     
17154     //private
17155     addicon : false,
17156     editicon: false,
17157     
17158     page: 0,
17159     hasQuery: false,
17160     append: false,
17161     loadNext: false,
17162     autoFocus : true,
17163     tickable : false,
17164     btnPosition : 'right',
17165     triggerList : true,
17166     showToggleBtn : true,
17167     animate : true,
17168     emptyResultText: 'Empty',
17169     triggerText : 'Select',
17170     emptyTitle : '',
17171     width : false,
17172     
17173     // element that contains real text value.. (when hidden is used..)
17174     
17175     getAutoCreate : function()
17176     {   
17177         var cfg = false;
17178         //render
17179         /*
17180          * Render classic select for iso
17181          */
17182         
17183         if(Roo.isIOS && this.useNativeIOS){
17184             cfg = this.getAutoCreateNativeIOS();
17185             return cfg;
17186         }
17187         
17188         /*
17189          * Touch Devices
17190          */
17191         
17192         if(Roo.isTouch && this.mobileTouchView){
17193             cfg = this.getAutoCreateTouchView();
17194             return cfg;;
17195         }
17196         
17197         /*
17198          *  Normal ComboBox
17199          */
17200         if(!this.tickable){
17201             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17202             return cfg;
17203         }
17204         
17205         /*
17206          *  ComboBox with tickable selections
17207          */
17208              
17209         var align = this.labelAlign || this.parentLabelAlign();
17210         
17211         cfg = {
17212             cls : 'form-group roo-combobox-tickable' //input-group
17213         };
17214         
17215         var btn_text_select = '';
17216         var btn_text_done = '';
17217         var btn_text_cancel = '';
17218         
17219         if (this.btn_text_show) {
17220             btn_text_select = 'Select';
17221             btn_text_done = 'Done';
17222             btn_text_cancel = 'Cancel'; 
17223         }
17224         
17225         var buttons = {
17226             tag : 'div',
17227             cls : 'tickable-buttons',
17228             cn : [
17229                 {
17230                     tag : 'button',
17231                     type : 'button',
17232                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17233                     //html : this.triggerText
17234                     html: btn_text_select
17235                 },
17236                 {
17237                     tag : 'button',
17238                     type : 'button',
17239                     name : 'ok',
17240                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17241                     //html : 'Done'
17242                     html: btn_text_done
17243                 },
17244                 {
17245                     tag : 'button',
17246                     type : 'button',
17247                     name : 'cancel',
17248                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17249                     //html : 'Cancel'
17250                     html: btn_text_cancel
17251                 }
17252             ]
17253         };
17254         
17255         if(this.editable){
17256             buttons.cn.unshift({
17257                 tag: 'input',
17258                 cls: 'roo-select2-search-field-input'
17259             });
17260         }
17261         
17262         var _this = this;
17263         
17264         Roo.each(buttons.cn, function(c){
17265             if (_this.size) {
17266                 c.cls += ' btn-' + _this.size;
17267             }
17268
17269             if (_this.disabled) {
17270                 c.disabled = true;
17271             }
17272         });
17273         
17274         var box = {
17275             tag: 'div',
17276             style : 'display: contents',
17277             cn: [
17278                 {
17279                     tag: 'input',
17280                     type : 'hidden',
17281                     cls: 'form-hidden-field'
17282                 },
17283                 {
17284                     tag: 'ul',
17285                     cls: 'roo-select2-choices',
17286                     cn:[
17287                         {
17288                             tag: 'li',
17289                             cls: 'roo-select2-search-field',
17290                             cn: [
17291                                 buttons
17292                             ]
17293                         }
17294                     ]
17295                 }
17296             ]
17297         };
17298         
17299         var combobox = {
17300             cls: 'roo-select2-container input-group roo-select2-container-multi',
17301             cn: [
17302                 
17303                 box
17304 //                {
17305 //                    tag: 'ul',
17306 //                    cls: 'typeahead typeahead-long dropdown-menu',
17307 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17308 //                }
17309             ]
17310         };
17311         
17312         if(this.hasFeedback && !this.allowBlank){
17313             
17314             var feedback = {
17315                 tag: 'span',
17316                 cls: 'glyphicon form-control-feedback'
17317             };
17318
17319             combobox.cn.push(feedback);
17320         }
17321         
17322         
17323         
17324         var indicator = {
17325             tag : 'i',
17326             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17327             tooltip : 'This field is required'
17328         };
17329         if (Roo.bootstrap.version == 4) {
17330             indicator = {
17331                 tag : 'i',
17332                 style : 'display:none'
17333             };
17334         }
17335         if (align ==='left' && this.fieldLabel.length) {
17336             
17337             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17338             
17339             cfg.cn = [
17340                 indicator,
17341                 {
17342                     tag: 'label',
17343                     'for' :  id,
17344                     cls : 'control-label col-form-label',
17345                     html : this.fieldLabel
17346
17347                 },
17348                 {
17349                     cls : "", 
17350                     cn: [
17351                         combobox
17352                     ]
17353                 }
17354
17355             ];
17356             
17357             var labelCfg = cfg.cn[1];
17358             var contentCfg = cfg.cn[2];
17359             
17360
17361             if(this.indicatorpos == 'right'){
17362                 
17363                 cfg.cn = [
17364                     {
17365                         tag: 'label',
17366                         'for' :  id,
17367                         cls : 'control-label col-form-label',
17368                         cn : [
17369                             {
17370                                 tag : 'span',
17371                                 html : this.fieldLabel
17372                             },
17373                             indicator
17374                         ]
17375                     },
17376                     {
17377                         cls : "",
17378                         cn: [
17379                             combobox
17380                         ]
17381                     }
17382
17383                 ];
17384                 
17385                 
17386                 
17387                 labelCfg = cfg.cn[0];
17388                 contentCfg = cfg.cn[1];
17389             
17390             }
17391             
17392             if(this.labelWidth > 12){
17393                 labelCfg.style = "width: " + this.labelWidth + 'px';
17394             }
17395             if(this.width * 1 > 0){
17396                 contentCfg.style = "width: " + this.width + 'px';
17397             }
17398             if(this.labelWidth < 13 && this.labelmd == 0){
17399                 this.labelmd = this.labelWidth;
17400             }
17401             
17402             if(this.labellg > 0){
17403                 labelCfg.cls += ' col-lg-' + this.labellg;
17404                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17405             }
17406             
17407             if(this.labelmd > 0){
17408                 labelCfg.cls += ' col-md-' + this.labelmd;
17409                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17410             }
17411             
17412             if(this.labelsm > 0){
17413                 labelCfg.cls += ' col-sm-' + this.labelsm;
17414                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17415             }
17416             
17417             if(this.labelxs > 0){
17418                 labelCfg.cls += ' col-xs-' + this.labelxs;
17419                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17420             }
17421                 
17422                 
17423         } else if ( this.fieldLabel.length) {
17424 //                Roo.log(" label");
17425                  cfg.cn = [
17426                    indicator,
17427                     {
17428                         tag: 'label',
17429                         //cls : 'input-group-addon',
17430                         html : this.fieldLabel
17431                     },
17432                     combobox
17433                 ];
17434                 
17435                 if(this.indicatorpos == 'right'){
17436                     cfg.cn = [
17437                         {
17438                             tag: 'label',
17439                             //cls : 'input-group-addon',
17440                             html : this.fieldLabel
17441                         },
17442                         indicator,
17443                         combobox
17444                     ];
17445                     
17446                 }
17447
17448         } else {
17449             
17450 //                Roo.log(" no label && no align");
17451                 cfg = combobox
17452                      
17453                 
17454         }
17455          
17456         var settings=this;
17457         ['xs','sm','md','lg'].map(function(size){
17458             if (settings[size]) {
17459                 cfg.cls += ' col-' + size + '-' + settings[size];
17460             }
17461         });
17462         
17463         return cfg;
17464         
17465     },
17466     
17467     _initEventsCalled : false,
17468     
17469     // private
17470     initEvents: function()
17471     {   
17472         if (this._initEventsCalled) { // as we call render... prevent looping...
17473             return;
17474         }
17475         this._initEventsCalled = true;
17476         
17477         if (!this.store) {
17478             throw "can not find store for combo";
17479         }
17480         
17481         this.indicator = this.indicatorEl();
17482         
17483         this.store = Roo.factory(this.store, Roo.data);
17484         this.store.parent = this;
17485         
17486         // if we are building from html. then this element is so complex, that we can not really
17487         // use the rendered HTML.
17488         // so we have to trash and replace the previous code.
17489         if (Roo.XComponent.build_from_html) {
17490             // remove this element....
17491             var e = this.el.dom, k=0;
17492             while (e ) { e = e.previousSibling;  ++k;}
17493
17494             this.el.remove();
17495             
17496             this.el=false;
17497             this.rendered = false;
17498             
17499             this.render(this.parent().getChildContainer(true), k);
17500         }
17501         
17502         if(Roo.isIOS && this.useNativeIOS){
17503             this.initIOSView();
17504             return;
17505         }
17506         
17507         /*
17508          * Touch Devices
17509          */
17510         
17511         if(Roo.isTouch && this.mobileTouchView){
17512             this.initTouchView();
17513             return;
17514         }
17515         
17516         if(this.tickable){
17517             this.initTickableEvents();
17518             return;
17519         }
17520         
17521         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17522         
17523         if(this.hiddenName){
17524             
17525             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17526             
17527             this.hiddenField.dom.value =
17528                 this.hiddenValue !== undefined ? this.hiddenValue :
17529                 this.value !== undefined ? this.value : '';
17530
17531             // prevent input submission
17532             this.el.dom.removeAttribute('name');
17533             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17534              
17535              
17536         }
17537         //if(Roo.isGecko){
17538         //    this.el.dom.setAttribute('autocomplete', 'off');
17539         //}
17540         
17541         var cls = 'x-combo-list';
17542         
17543         //this.list = new Roo.Layer({
17544         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17545         //});
17546         
17547         var _this = this;
17548         
17549         (function(){
17550             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17551             _this.list.setWidth(lw);
17552         }).defer(100);
17553         
17554         this.list.on('mouseover', this.onViewOver, this);
17555         this.list.on('mousemove', this.onViewMove, this);
17556         this.list.on('scroll', this.onViewScroll, this);
17557         
17558         /*
17559         this.list.swallowEvent('mousewheel');
17560         this.assetHeight = 0;
17561
17562         if(this.title){
17563             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17564             this.assetHeight += this.header.getHeight();
17565         }
17566
17567         this.innerList = this.list.createChild({cls:cls+'-inner'});
17568         this.innerList.on('mouseover', this.onViewOver, this);
17569         this.innerList.on('mousemove', this.onViewMove, this);
17570         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17571         
17572         if(this.allowBlank && !this.pageSize && !this.disableClear){
17573             this.footer = this.list.createChild({cls:cls+'-ft'});
17574             this.pageTb = new Roo.Toolbar(this.footer);
17575            
17576         }
17577         if(this.pageSize){
17578             this.footer = this.list.createChild({cls:cls+'-ft'});
17579             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17580                     {pageSize: this.pageSize});
17581             
17582         }
17583         
17584         if (this.pageTb && this.allowBlank && !this.disableClear) {
17585             var _this = this;
17586             this.pageTb.add(new Roo.Toolbar.Fill(), {
17587                 cls: 'x-btn-icon x-btn-clear',
17588                 text: '&#160;',
17589                 handler: function()
17590                 {
17591                     _this.collapse();
17592                     _this.clearValue();
17593                     _this.onSelect(false, -1);
17594                 }
17595             });
17596         }
17597         if (this.footer) {
17598             this.assetHeight += this.footer.getHeight();
17599         }
17600         */
17601             
17602         if(!this.tpl){
17603             this.tpl = Roo.bootstrap.version == 4 ?
17604                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17605                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17606         }
17607
17608         this.view = new Roo.View(this.list, this.tpl, {
17609             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17610         });
17611         //this.view.wrapEl.setDisplayed(false);
17612         this.view.on('click', this.onViewClick, this);
17613         
17614         
17615         this.store.on('beforeload', this.onBeforeLoad, this);
17616         this.store.on('load', this.onLoad, this);
17617         this.store.on('loadexception', this.onLoadException, this);
17618         /*
17619         if(this.resizable){
17620             this.resizer = new Roo.Resizable(this.list,  {
17621                pinned:true, handles:'se'
17622             });
17623             this.resizer.on('resize', function(r, w, h){
17624                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17625                 this.listWidth = w;
17626                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17627                 this.restrictHeight();
17628             }, this);
17629             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17630         }
17631         */
17632         if(!this.editable){
17633             this.editable = true;
17634             this.setEditable(false);
17635         }
17636         
17637         /*
17638         
17639         if (typeof(this.events.add.listeners) != 'undefined') {
17640             
17641             this.addicon = this.wrap.createChild(
17642                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17643        
17644             this.addicon.on('click', function(e) {
17645                 this.fireEvent('add', this);
17646             }, this);
17647         }
17648         if (typeof(this.events.edit.listeners) != 'undefined') {
17649             
17650             this.editicon = this.wrap.createChild(
17651                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17652             if (this.addicon) {
17653                 this.editicon.setStyle('margin-left', '40px');
17654             }
17655             this.editicon.on('click', function(e) {
17656                 
17657                 // we fire even  if inothing is selected..
17658                 this.fireEvent('edit', this, this.lastData );
17659                 
17660             }, this);
17661         }
17662         */
17663         
17664         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17665             "up" : function(e){
17666                 this.inKeyMode = true;
17667                 this.selectPrev();
17668             },
17669
17670             "down" : function(e){
17671                 if(!this.isExpanded()){
17672                     this.onTriggerClick();
17673                 }else{
17674                     this.inKeyMode = true;
17675                     this.selectNext();
17676                 }
17677             },
17678
17679             "enter" : function(e){
17680 //                this.onViewClick();
17681                 //return true;
17682                 this.collapse();
17683                 
17684                 if(this.fireEvent("specialkey", this, e)){
17685                     this.onViewClick(false);
17686                 }
17687                 
17688                 return true;
17689             },
17690
17691             "esc" : function(e){
17692                 this.collapse();
17693             },
17694
17695             "tab" : function(e){
17696                 this.collapse();
17697                 
17698                 if(this.fireEvent("specialkey", this, e)){
17699                     this.onViewClick(false);
17700                 }
17701                 
17702                 return true;
17703             },
17704
17705             scope : this,
17706
17707             doRelay : function(foo, bar, hname){
17708                 if(hname == 'down' || this.scope.isExpanded()){
17709                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17710                 }
17711                 return true;
17712             },
17713
17714             forceKeyDown: true
17715         });
17716         
17717         
17718         this.queryDelay = Math.max(this.queryDelay || 10,
17719                 this.mode == 'local' ? 10 : 250);
17720         
17721         
17722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17723         
17724         if(this.typeAhead){
17725             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17726         }
17727         if(this.editable !== false){
17728             this.inputEl().on("keyup", this.onKeyUp, this);
17729         }
17730         if(this.forceSelection){
17731             this.inputEl().on('blur', this.doForce, this);
17732         }
17733         
17734         if(this.multiple){
17735             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17736             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17737         }
17738     },
17739     
17740     initTickableEvents: function()
17741     {   
17742         this.createList();
17743         
17744         if(this.hiddenName){
17745             
17746             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17747             
17748             this.hiddenField.dom.value =
17749                 this.hiddenValue !== undefined ? this.hiddenValue :
17750                 this.value !== undefined ? this.value : '';
17751
17752             // prevent input submission
17753             this.el.dom.removeAttribute('name');
17754             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17755              
17756              
17757         }
17758         
17759 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17760         
17761         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17762         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17763         if(this.triggerList){
17764             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17765         }
17766          
17767         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17768         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17769         
17770         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17771         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17772         
17773         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17774         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17775         
17776         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17777         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17778         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779         
17780         this.okBtn.hide();
17781         this.cancelBtn.hide();
17782         
17783         var _this = this;
17784         
17785         (function(){
17786             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17787             _this.list.setWidth(lw);
17788         }).defer(100);
17789         
17790         this.list.on('mouseover', this.onViewOver, this);
17791         this.list.on('mousemove', this.onViewMove, this);
17792         
17793         this.list.on('scroll', this.onViewScroll, this);
17794         
17795         if(!this.tpl){
17796             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17797                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17798         }
17799
17800         this.view = new Roo.View(this.list, this.tpl, {
17801             singleSelect:true,
17802             tickable:true,
17803             parent:this,
17804             store: this.store,
17805             selectedClass: this.selectedClass
17806         });
17807         
17808         //this.view.wrapEl.setDisplayed(false);
17809         this.view.on('click', this.onViewClick, this);
17810         
17811         
17812         
17813         this.store.on('beforeload', this.onBeforeLoad, this);
17814         this.store.on('load', this.onLoad, this);
17815         this.store.on('loadexception', this.onLoadException, this);
17816         
17817         if(this.editable){
17818             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17819                 "up" : function(e){
17820                     this.inKeyMode = true;
17821                     this.selectPrev();
17822                 },
17823
17824                 "down" : function(e){
17825                     this.inKeyMode = true;
17826                     this.selectNext();
17827                 },
17828
17829                 "enter" : function(e){
17830                     if(this.fireEvent("specialkey", this, e)){
17831                         this.onViewClick(false);
17832                     }
17833                     
17834                     return true;
17835                 },
17836
17837                 "esc" : function(e){
17838                     this.onTickableFooterButtonClick(e, false, false);
17839                 },
17840
17841                 "tab" : function(e){
17842                     this.fireEvent("specialkey", this, e);
17843                     
17844                     this.onTickableFooterButtonClick(e, false, false);
17845                     
17846                     return true;
17847                 },
17848
17849                 scope : this,
17850
17851                 doRelay : function(e, fn, key){
17852                     if(this.scope.isExpanded()){
17853                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17854                     }
17855                     return true;
17856                 },
17857
17858                 forceKeyDown: true
17859             });
17860         }
17861         
17862         this.queryDelay = Math.max(this.queryDelay || 10,
17863                 this.mode == 'local' ? 10 : 250);
17864         
17865         
17866         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17867         
17868         if(this.typeAhead){
17869             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17870         }
17871         
17872         if(this.editable !== false){
17873             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17874         }
17875         
17876         this.indicator = this.indicatorEl();
17877         
17878         if(this.indicator){
17879             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17880             this.indicator.hide();
17881         }
17882         
17883     },
17884
17885     onDestroy : function(){
17886         if(this.view){
17887             this.view.setStore(null);
17888             this.view.el.removeAllListeners();
17889             this.view.el.remove();
17890             this.view.purgeListeners();
17891         }
17892         if(this.list){
17893             this.list.dom.innerHTML  = '';
17894         }
17895         
17896         if(this.store){
17897             this.store.un('beforeload', this.onBeforeLoad, this);
17898             this.store.un('load', this.onLoad, this);
17899             this.store.un('loadexception', this.onLoadException, this);
17900         }
17901         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17902     },
17903
17904     // private
17905     fireKey : function(e){
17906         if(e.isNavKeyPress() && !this.list.isVisible()){
17907             this.fireEvent("specialkey", this, e);
17908         }
17909     },
17910
17911     // private
17912     onResize: function(w, h)
17913     {
17914         
17915         
17916 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17917 //        
17918 //        if(typeof w != 'number'){
17919 //            // we do not handle it!?!?
17920 //            return;
17921 //        }
17922 //        var tw = this.trigger.getWidth();
17923 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17924 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17925 //        var x = w - tw;
17926 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17927 //            
17928 //        //this.trigger.setStyle('left', x+'px');
17929 //        
17930 //        if(this.list && this.listWidth === undefined){
17931 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17932 //            this.list.setWidth(lw);
17933 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17934 //        }
17935         
17936     
17937         
17938     },
17939
17940     /**
17941      * Allow or prevent the user from directly editing the field text.  If false is passed,
17942      * the user will only be able to select from the items defined in the dropdown list.  This method
17943      * is the runtime equivalent of setting the 'editable' config option at config time.
17944      * @param {Boolean} value True to allow the user to directly edit the field text
17945      */
17946     setEditable : function(value){
17947         if(value == this.editable){
17948             return;
17949         }
17950         this.editable = value;
17951         if(!value){
17952             this.inputEl().dom.setAttribute('readOnly', true);
17953             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17954             this.inputEl().addClass('x-combo-noedit');
17955         }else{
17956             this.inputEl().dom.removeAttribute('readOnly');
17957             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17958             this.inputEl().removeClass('x-combo-noedit');
17959         }
17960     },
17961
17962     // private
17963     
17964     onBeforeLoad : function(combo,opts){
17965         if(!this.hasFocus){
17966             return;
17967         }
17968          if (!opts.add) {
17969             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17970          }
17971         this.restrictHeight();
17972         this.selectedIndex = -1;
17973     },
17974
17975     // private
17976     onLoad : function(){
17977         
17978         this.hasQuery = false;
17979         
17980         if(!this.hasFocus){
17981             return;
17982         }
17983         
17984         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17985             this.loading.hide();
17986         }
17987         
17988         if(this.store.getCount() > 0){
17989             
17990             this.expand();
17991             this.restrictHeight();
17992             if(this.lastQuery == this.allQuery){
17993                 if(this.editable && !this.tickable){
17994                     this.inputEl().dom.select();
17995                 }
17996                 
17997                 if(
17998                     !this.selectByValue(this.value, true) &&
17999                     this.autoFocus && 
18000                     (
18001                         !this.store.lastOptions ||
18002                         typeof(this.store.lastOptions.add) == 'undefined' || 
18003                         this.store.lastOptions.add != true
18004                     )
18005                 ){
18006                     this.select(0, true);
18007                 }
18008             }else{
18009                 if(this.autoFocus){
18010                     this.selectNext();
18011                 }
18012                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18013                     this.taTask.delay(this.typeAheadDelay);
18014                 }
18015             }
18016         }else{
18017             this.onEmptyResults();
18018         }
18019         
18020         //this.el.focus();
18021     },
18022     // private
18023     onLoadException : function()
18024     {
18025         this.hasQuery = false;
18026         
18027         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18028             this.loading.hide();
18029         }
18030         
18031         if(this.tickable && this.editable){
18032             return;
18033         }
18034         
18035         this.collapse();
18036         // only causes errors at present
18037         //Roo.log(this.store.reader.jsonData);
18038         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18039             // fixme
18040             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18041         //}
18042         
18043         
18044     },
18045     // private
18046     onTypeAhead : function(){
18047         if(this.store.getCount() > 0){
18048             var r = this.store.getAt(0);
18049             var newValue = r.data[this.displayField];
18050             var len = newValue.length;
18051             var selStart = this.getRawValue().length;
18052             
18053             if(selStart != len){
18054                 this.setRawValue(newValue);
18055                 this.selectText(selStart, newValue.length);
18056             }
18057         }
18058     },
18059
18060     // private
18061     onSelect : function(record, index){
18062         
18063         if(this.fireEvent('beforeselect', this, record, index) !== false){
18064         
18065             this.setFromData(index > -1 ? record.data : false);
18066             
18067             this.collapse();
18068             this.fireEvent('select', this, record, index);
18069         }
18070     },
18071
18072     /**
18073      * Returns the currently selected field value or empty string if no value is set.
18074      * @return {String} value The selected value
18075      */
18076     getValue : function()
18077     {
18078         if(Roo.isIOS && this.useNativeIOS){
18079             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18080         }
18081         
18082         if(this.multiple){
18083             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18084         }
18085         
18086         if(this.valueField){
18087             return typeof this.value != 'undefined' ? this.value : '';
18088         }else{
18089             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18090         }
18091     },
18092     
18093     getRawValue : function()
18094     {
18095         if(Roo.isIOS && this.useNativeIOS){
18096             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18097         }
18098         
18099         var v = this.inputEl().getValue();
18100         
18101         return v;
18102     },
18103
18104     /**
18105      * Clears any text/value currently set in the field
18106      */
18107     clearValue : function(){
18108         
18109         if(this.hiddenField){
18110             this.hiddenField.dom.value = '';
18111         }
18112         this.value = '';
18113         this.setRawValue('');
18114         this.lastSelectionText = '';
18115         this.lastData = false;
18116         
18117         var close = this.closeTriggerEl();
18118         
18119         if(close){
18120             close.hide();
18121         }
18122         
18123         this.validate();
18124         
18125     },
18126
18127     /**
18128      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18129      * will be displayed in the field.  If the value does not match the data value of an existing item,
18130      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18131      * Otherwise the field will be blank (although the value will still be set).
18132      * @param {String} value The value to match
18133      */
18134     setValue : function(v)
18135     {
18136         if(Roo.isIOS && this.useNativeIOS){
18137             this.setIOSValue(v);
18138             return;
18139         }
18140         
18141         if(this.multiple){
18142             this.syncValue();
18143             return;
18144         }
18145         
18146         var text = v;
18147         if(this.valueField){
18148             var r = this.findRecord(this.valueField, v);
18149             if(r){
18150                 text = r.data[this.displayField];
18151             }else if(this.valueNotFoundText !== undefined){
18152                 text = this.valueNotFoundText;
18153             }
18154         }
18155         this.lastSelectionText = text;
18156         if(this.hiddenField){
18157             this.hiddenField.dom.value = v;
18158         }
18159         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18160         this.value = v;
18161         
18162         var close = this.closeTriggerEl();
18163         
18164         if(close){
18165             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18166         }
18167         
18168         this.validate();
18169     },
18170     /**
18171      * @property {Object} the last set data for the element
18172      */
18173     
18174     lastData : false,
18175     /**
18176      * Sets the value of the field based on a object which is related to the record format for the store.
18177      * @param {Object} value the value to set as. or false on reset?
18178      */
18179     setFromData : function(o){
18180         
18181         if(this.multiple){
18182             this.addItem(o);
18183             return;
18184         }
18185             
18186         var dv = ''; // display value
18187         var vv = ''; // value value..
18188         this.lastData = o;
18189         if (this.displayField) {
18190             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18191         } else {
18192             // this is an error condition!!!
18193             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18194         }
18195         
18196         if(this.valueField){
18197             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18198         }
18199         
18200         var close = this.closeTriggerEl();
18201         
18202         if(close){
18203             if(dv.length || vv * 1 > 0){
18204                 close.show() ;
18205                 this.blockFocus=true;
18206             } else {
18207                 close.hide();
18208             }             
18209         }
18210         
18211         if(this.hiddenField){
18212             this.hiddenField.dom.value = vv;
18213             
18214             this.lastSelectionText = dv;
18215             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18216             this.value = vv;
18217             return;
18218         }
18219         // no hidden field.. - we store the value in 'value', but still display
18220         // display field!!!!
18221         this.lastSelectionText = dv;
18222         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18223         this.value = vv;
18224         
18225         
18226         
18227     },
18228     // private
18229     reset : function(){
18230         // overridden so that last data is reset..
18231         
18232         if(this.multiple){
18233             this.clearItem();
18234             return;
18235         }
18236         
18237         this.setValue(this.originalValue);
18238         //this.clearInvalid();
18239         this.lastData = false;
18240         if (this.view) {
18241             this.view.clearSelections();
18242         }
18243         
18244         this.validate();
18245     },
18246     // private
18247     findRecord : function(prop, value){
18248         var record;
18249         if(this.store.getCount() > 0){
18250             this.store.each(function(r){
18251                 if(r.data[prop] == value){
18252                     record = r;
18253                     return false;
18254                 }
18255                 return true;
18256             });
18257         }
18258         return record;
18259     },
18260     
18261     getName: function()
18262     {
18263         // returns hidden if it's set..
18264         if (!this.rendered) {return ''};
18265         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18266         
18267     },
18268     // private
18269     onViewMove : function(e, t){
18270         this.inKeyMode = false;
18271     },
18272
18273     // private
18274     onViewOver : function(e, t){
18275         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18276             return;
18277         }
18278         var item = this.view.findItemFromChild(t);
18279         
18280         if(item){
18281             var index = this.view.indexOf(item);
18282             this.select(index, false);
18283         }
18284     },
18285
18286     // private
18287     onViewClick : function(view, doFocus, el, e)
18288     {
18289         var index = this.view.getSelectedIndexes()[0];
18290         
18291         var r = this.store.getAt(index);
18292         
18293         if(this.tickable){
18294             
18295             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18296                 return;
18297             }
18298             
18299             var rm = false;
18300             var _this = this;
18301             
18302             Roo.each(this.tickItems, function(v,k){
18303                 
18304                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18305                     Roo.log(v);
18306                     _this.tickItems.splice(k, 1);
18307                     
18308                     if(typeof(e) == 'undefined' && view == false){
18309                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18310                     }
18311                     
18312                     rm = true;
18313                     return;
18314                 }
18315             });
18316             
18317             if(rm){
18318                 return;
18319             }
18320             
18321             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18322                 this.tickItems.push(r.data);
18323             }
18324             
18325             if(typeof(e) == 'undefined' && view == false){
18326                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18327             }
18328                     
18329             return;
18330         }
18331         
18332         if(r){
18333             this.onSelect(r, index);
18334         }
18335         if(doFocus !== false && !this.blockFocus){
18336             this.inputEl().focus();
18337         }
18338     },
18339
18340     // private
18341     restrictHeight : function(){
18342         //this.innerList.dom.style.height = '';
18343         //var inner = this.innerList.dom;
18344         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18345         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18346         //this.list.beginUpdate();
18347         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18348         this.list.alignTo(this.inputEl(), this.listAlign);
18349         this.list.alignTo(this.inputEl(), this.listAlign);
18350         //this.list.endUpdate();
18351     },
18352
18353     // private
18354     onEmptyResults : function(){
18355         
18356         if(this.tickable && this.editable){
18357             this.hasFocus = false;
18358             this.restrictHeight();
18359             return;
18360         }
18361         
18362         this.collapse();
18363     },
18364
18365     /**
18366      * Returns true if the dropdown list is expanded, else false.
18367      */
18368     isExpanded : function(){
18369         return this.list.isVisible();
18370     },
18371
18372     /**
18373      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18374      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18375      * @param {String} value The data value of the item to select
18376      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18377      * selected item if it is not currently in view (defaults to true)
18378      * @return {Boolean} True if the value matched an item in the list, else false
18379      */
18380     selectByValue : function(v, scrollIntoView){
18381         if(v !== undefined && v !== null){
18382             var r = this.findRecord(this.valueField || this.displayField, v);
18383             if(r){
18384                 this.select(this.store.indexOf(r), scrollIntoView);
18385                 return true;
18386             }
18387         }
18388         return false;
18389     },
18390
18391     /**
18392      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18393      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18394      * @param {Number} index The zero-based index of the list item to select
18395      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18396      * selected item if it is not currently in view (defaults to true)
18397      */
18398     select : function(index, scrollIntoView){
18399         this.selectedIndex = index;
18400         this.view.select(index);
18401         if(scrollIntoView !== false){
18402             var el = this.view.getNode(index);
18403             /*
18404              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18405              */
18406             if(el){
18407                 this.list.scrollChildIntoView(el, false);
18408             }
18409         }
18410     },
18411
18412     // private
18413     selectNext : function(){
18414         var ct = this.store.getCount();
18415         if(ct > 0){
18416             if(this.selectedIndex == -1){
18417                 this.select(0);
18418             }else if(this.selectedIndex < ct-1){
18419                 this.select(this.selectedIndex+1);
18420             }
18421         }
18422     },
18423
18424     // private
18425     selectPrev : function(){
18426         var ct = this.store.getCount();
18427         if(ct > 0){
18428             if(this.selectedIndex == -1){
18429                 this.select(0);
18430             }else if(this.selectedIndex != 0){
18431                 this.select(this.selectedIndex-1);
18432             }
18433         }
18434     },
18435
18436     // private
18437     onKeyUp : function(e){
18438         if(this.editable !== false && !e.isSpecialKey()){
18439             this.lastKey = e.getKey();
18440             this.dqTask.delay(this.queryDelay);
18441         }
18442     },
18443
18444     // private
18445     validateBlur : function(){
18446         return !this.list || !this.list.isVisible();   
18447     },
18448
18449     // private
18450     initQuery : function(){
18451         
18452         var v = this.getRawValue();
18453         
18454         if(this.tickable && this.editable){
18455             v = this.tickableInputEl().getValue();
18456         }
18457         
18458         this.doQuery(v);
18459     },
18460
18461     // private
18462     doForce : function(){
18463         if(this.inputEl().dom.value.length > 0){
18464             this.inputEl().dom.value =
18465                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18466              
18467         }
18468     },
18469
18470     /**
18471      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18472      * query allowing the query action to be canceled if needed.
18473      * @param {String} query The SQL query to execute
18474      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18475      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18476      * saved in the current store (defaults to false)
18477      */
18478     doQuery : function(q, forceAll){
18479         
18480         if(q === undefined || q === null){
18481             q = '';
18482         }
18483         var qe = {
18484             query: q,
18485             forceAll: forceAll,
18486             combo: this,
18487             cancel:false
18488         };
18489         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18490             return false;
18491         }
18492         q = qe.query;
18493         
18494         forceAll = qe.forceAll;
18495         if(forceAll === true || (q.length >= this.minChars)){
18496             
18497             this.hasQuery = true;
18498             
18499             if(this.lastQuery != q || this.alwaysQuery){
18500                 this.lastQuery = q;
18501                 if(this.mode == 'local'){
18502                     this.selectedIndex = -1;
18503                     if(forceAll){
18504                         this.store.clearFilter();
18505                     }else{
18506                         
18507                         if(this.specialFilter){
18508                             this.fireEvent('specialfilter', this);
18509                             this.onLoad();
18510                             return;
18511                         }
18512                         
18513                         this.store.filter(this.displayField, q);
18514                     }
18515                     
18516                     this.store.fireEvent("datachanged", this.store);
18517                     
18518                     this.onLoad();
18519                     
18520                     
18521                 }else{
18522                     
18523                     this.store.baseParams[this.queryParam] = q;
18524                     
18525                     var options = {params : this.getParams(q)};
18526                     
18527                     if(this.loadNext){
18528                         options.add = true;
18529                         options.params.start = this.page * this.pageSize;
18530                     }
18531                     
18532                     this.store.load(options);
18533                     
18534                     /*
18535                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18536                      *  we should expand the list on onLoad
18537                      *  so command out it
18538                      */
18539 //                    this.expand();
18540                 }
18541             }else{
18542                 this.selectedIndex = -1;
18543                 this.onLoad();   
18544             }
18545         }
18546         
18547         this.loadNext = false;
18548     },
18549     
18550     // private
18551     getParams : function(q){
18552         var p = {};
18553         //p[this.queryParam] = q;
18554         
18555         if(this.pageSize){
18556             p.start = 0;
18557             p.limit = this.pageSize;
18558         }
18559         return p;
18560     },
18561
18562     /**
18563      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18564      */
18565     collapse : function(){
18566         if(!this.isExpanded()){
18567             return;
18568         }
18569         
18570         this.list.hide();
18571         
18572         this.hasFocus = false;
18573         
18574         if(this.tickable){
18575             this.okBtn.hide();
18576             this.cancelBtn.hide();
18577             this.trigger.show();
18578             
18579             if(this.editable){
18580                 this.tickableInputEl().dom.value = '';
18581                 this.tickableInputEl().blur();
18582             }
18583             
18584         }
18585         
18586         Roo.get(document).un('mousedown', this.collapseIf, this);
18587         Roo.get(document).un('mousewheel', this.collapseIf, this);
18588         if (!this.editable) {
18589             Roo.get(document).un('keydown', this.listKeyPress, this);
18590         }
18591         this.fireEvent('collapse', this);
18592         
18593         this.validate();
18594     },
18595
18596     // private
18597     collapseIf : function(e){
18598         var in_combo  = e.within(this.el);
18599         var in_list =  e.within(this.list);
18600         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18601         
18602         if (in_combo || in_list || is_list) {
18603             //e.stopPropagation();
18604             return;
18605         }
18606         
18607         if(this.tickable){
18608             this.onTickableFooterButtonClick(e, false, false);
18609         }
18610
18611         this.collapse();
18612         
18613     },
18614
18615     /**
18616      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18617      */
18618     expand : function(){
18619        
18620         if(this.isExpanded() || !this.hasFocus){
18621             return;
18622         }
18623         
18624         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18625         this.list.setWidth(lw);
18626         
18627         Roo.log('expand');
18628         
18629         this.list.show();
18630         
18631         this.restrictHeight();
18632         
18633         if(this.tickable){
18634             
18635             this.tickItems = Roo.apply([], this.item);
18636             
18637             this.okBtn.show();
18638             this.cancelBtn.show();
18639             this.trigger.hide();
18640             
18641             if(this.editable){
18642                 this.tickableInputEl().focus();
18643             }
18644             
18645         }
18646         
18647         Roo.get(document).on('mousedown', this.collapseIf, this);
18648         Roo.get(document).on('mousewheel', this.collapseIf, this);
18649         if (!this.editable) {
18650             Roo.get(document).on('keydown', this.listKeyPress, this);
18651         }
18652         
18653         this.fireEvent('expand', this);
18654     },
18655
18656     // private
18657     // Implements the default empty TriggerField.onTriggerClick function
18658     onTriggerClick : function(e)
18659     {
18660         Roo.log('trigger click');
18661         
18662         if(this.disabled || !this.triggerList){
18663             return;
18664         }
18665         
18666         this.page = 0;
18667         this.loadNext = false;
18668         
18669         if(this.isExpanded()){
18670             this.collapse();
18671             if (!this.blockFocus) {
18672                 this.inputEl().focus();
18673             }
18674             
18675         }else {
18676             this.hasFocus = true;
18677             if(this.triggerAction == 'all') {
18678                 this.doQuery(this.allQuery, true);
18679             } else {
18680                 this.doQuery(this.getRawValue());
18681             }
18682             if (!this.blockFocus) {
18683                 this.inputEl().focus();
18684             }
18685         }
18686     },
18687     
18688     onTickableTriggerClick : function(e)
18689     {
18690         if(this.disabled){
18691             return;
18692         }
18693         
18694         this.page = 0;
18695         this.loadNext = false;
18696         this.hasFocus = true;
18697         
18698         if(this.triggerAction == 'all') {
18699             this.doQuery(this.allQuery, true);
18700         } else {
18701             this.doQuery(this.getRawValue());
18702         }
18703     },
18704     
18705     onSearchFieldClick : function(e)
18706     {
18707         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18708             this.onTickableFooterButtonClick(e, false, false);
18709             return;
18710         }
18711         
18712         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18713             return;
18714         }
18715         
18716         this.page = 0;
18717         this.loadNext = false;
18718         this.hasFocus = true;
18719         
18720         if(this.triggerAction == 'all') {
18721             this.doQuery(this.allQuery, true);
18722         } else {
18723             this.doQuery(this.getRawValue());
18724         }
18725     },
18726     
18727     listKeyPress : function(e)
18728     {
18729         //Roo.log('listkeypress');
18730         // scroll to first matching element based on key pres..
18731         if (e.isSpecialKey()) {
18732             return false;
18733         }
18734         var k = String.fromCharCode(e.getKey()).toUpperCase();
18735         //Roo.log(k);
18736         var match  = false;
18737         var csel = this.view.getSelectedNodes();
18738         var cselitem = false;
18739         if (csel.length) {
18740             var ix = this.view.indexOf(csel[0]);
18741             cselitem  = this.store.getAt(ix);
18742             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18743                 cselitem = false;
18744             }
18745             
18746         }
18747         
18748         this.store.each(function(v) { 
18749             if (cselitem) {
18750                 // start at existing selection.
18751                 if (cselitem.id == v.id) {
18752                     cselitem = false;
18753                 }
18754                 return true;
18755             }
18756                 
18757             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18758                 match = this.store.indexOf(v);
18759                 return false;
18760             }
18761             return true;
18762         }, this);
18763         
18764         if (match === false) {
18765             return true; // no more action?
18766         }
18767         // scroll to?
18768         this.view.select(match);
18769         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18770         sn.scrollIntoView(sn.dom.parentNode, false);
18771     },
18772     
18773     onViewScroll : function(e, t){
18774         
18775         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){
18776             return;
18777         }
18778         
18779         this.hasQuery = true;
18780         
18781         this.loading = this.list.select('.loading', true).first();
18782         
18783         if(this.loading === null){
18784             this.list.createChild({
18785                 tag: 'div',
18786                 cls: 'loading roo-select2-more-results roo-select2-active',
18787                 html: 'Loading more results...'
18788             });
18789             
18790             this.loading = this.list.select('.loading', true).first();
18791             
18792             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18793             
18794             this.loading.hide();
18795         }
18796         
18797         this.loading.show();
18798         
18799         var _combo = this;
18800         
18801         this.page++;
18802         this.loadNext = true;
18803         
18804         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18805         
18806         return;
18807     },
18808     
18809     addItem : function(o)
18810     {   
18811         var dv = ''; // display value
18812         
18813         if (this.displayField) {
18814             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18815         } else {
18816             // this is an error condition!!!
18817             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18818         }
18819         
18820         if(!dv.length){
18821             return;
18822         }
18823         
18824         var choice = this.choices.createChild({
18825             tag: 'li',
18826             cls: 'roo-select2-search-choice',
18827             cn: [
18828                 {
18829                     tag: 'div',
18830                     html: dv
18831                 },
18832                 {
18833                     tag: 'a',
18834                     href: '#',
18835                     cls: 'roo-select2-search-choice-close fa fa-times',
18836                     tabindex: '-1'
18837                 }
18838             ]
18839             
18840         }, this.searchField);
18841         
18842         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18843         
18844         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18845         
18846         this.item.push(o);
18847         
18848         this.lastData = o;
18849         
18850         this.syncValue();
18851         
18852         this.inputEl().dom.value = '';
18853         
18854         this.validate();
18855     },
18856     
18857     onRemoveItem : function(e, _self, o)
18858     {
18859         e.preventDefault();
18860         
18861         this.lastItem = Roo.apply([], this.item);
18862         
18863         var index = this.item.indexOf(o.data) * 1;
18864         
18865         if( index < 0){
18866             Roo.log('not this item?!');
18867             return;
18868         }
18869         
18870         this.item.splice(index, 1);
18871         o.item.remove();
18872         
18873         this.syncValue();
18874         
18875         this.fireEvent('remove', this, e);
18876         
18877         this.validate();
18878         
18879     },
18880     
18881     syncValue : function()
18882     {
18883         if(!this.item.length){
18884             this.clearValue();
18885             return;
18886         }
18887             
18888         var value = [];
18889         var _this = this;
18890         Roo.each(this.item, function(i){
18891             if(_this.valueField){
18892                 value.push(i[_this.valueField]);
18893                 return;
18894             }
18895
18896             value.push(i);
18897         });
18898
18899         this.value = value.join(',');
18900
18901         if(this.hiddenField){
18902             this.hiddenField.dom.value = this.value;
18903         }
18904         
18905         this.store.fireEvent("datachanged", this.store);
18906         
18907         this.validate();
18908     },
18909     
18910     clearItem : function()
18911     {
18912         if(!this.multiple){
18913             return;
18914         }
18915         
18916         this.item = [];
18917         
18918         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18919            c.remove();
18920         });
18921         
18922         this.syncValue();
18923         
18924         this.validate();
18925         
18926         if(this.tickable && !Roo.isTouch){
18927             this.view.refresh();
18928         }
18929     },
18930     
18931     inputEl: function ()
18932     {
18933         if(Roo.isIOS && this.useNativeIOS){
18934             return this.el.select('select.roo-ios-select', true).first();
18935         }
18936         
18937         if(Roo.isTouch && this.mobileTouchView){
18938             return this.el.select('input.form-control',true).first();
18939         }
18940         
18941         if(this.tickable){
18942             return this.searchField;
18943         }
18944         
18945         return this.el.select('input.form-control',true).first();
18946     },
18947     
18948     onTickableFooterButtonClick : function(e, btn, el)
18949     {
18950         e.preventDefault();
18951         
18952         this.lastItem = Roo.apply([], this.item);
18953         
18954         if(btn && btn.name == 'cancel'){
18955             this.tickItems = Roo.apply([], this.item);
18956             this.collapse();
18957             return;
18958         }
18959         
18960         this.clearItem();
18961         
18962         var _this = this;
18963         
18964         Roo.each(this.tickItems, function(o){
18965             _this.addItem(o);
18966         });
18967         
18968         this.collapse();
18969         
18970     },
18971     
18972     validate : function()
18973     {
18974         if(this.getVisibilityEl().hasClass('hidden')){
18975             return true;
18976         }
18977         
18978         var v = this.getRawValue();
18979         
18980         if(this.multiple){
18981             v = this.getValue();
18982         }
18983         
18984         if(this.disabled || this.allowBlank || v.length){
18985             this.markValid();
18986             return true;
18987         }
18988         
18989         this.markInvalid();
18990         return false;
18991     },
18992     
18993     tickableInputEl : function()
18994     {
18995         if(!this.tickable || !this.editable){
18996             return this.inputEl();
18997         }
18998         
18999         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19000     },
19001     
19002     
19003     getAutoCreateTouchView : function()
19004     {
19005         var id = Roo.id();
19006         
19007         var cfg = {
19008             cls: 'form-group' //input-group
19009         };
19010         
19011         var input =  {
19012             tag: 'input',
19013             id : id,
19014             type : this.inputType,
19015             cls : 'form-control x-combo-noedit',
19016             autocomplete: 'new-password',
19017             placeholder : this.placeholder || '',
19018             readonly : true
19019         };
19020         
19021         if (this.name) {
19022             input.name = this.name;
19023         }
19024         
19025         if (this.size) {
19026             input.cls += ' input-' + this.size;
19027         }
19028         
19029         if (this.disabled) {
19030             input.disabled = true;
19031         }
19032         
19033         var inputblock = {
19034             cls : 'roo-combobox-wrap',
19035             cn : [
19036                 input
19037             ]
19038         };
19039         
19040         if(this.before){
19041             inputblock.cls += ' input-group';
19042             
19043             inputblock.cn.unshift({
19044                 tag :'span',
19045                 cls : 'input-group-addon input-group-prepend input-group-text',
19046                 html : this.before
19047             });
19048         }
19049         
19050         if(this.removable && !this.multiple){
19051             inputblock.cls += ' roo-removable';
19052             
19053             inputblock.cn.push({
19054                 tag: 'button',
19055                 html : 'x',
19056                 cls : 'roo-combo-removable-btn close'
19057             });
19058         }
19059
19060         if(this.hasFeedback && !this.allowBlank){
19061             
19062             inputblock.cls += ' has-feedback';
19063             
19064             inputblock.cn.push({
19065                 tag: 'span',
19066                 cls: 'glyphicon form-control-feedback'
19067             });
19068             
19069         }
19070         
19071         if (this.after) {
19072             
19073             inputblock.cls += (this.before) ? '' : ' input-group';
19074             
19075             inputblock.cn.push({
19076                 tag :'span',
19077                 cls : 'input-group-addon input-group-append input-group-text',
19078                 html : this.after
19079             });
19080         }
19081
19082         
19083         var ibwrap = inputblock;
19084         
19085         if(this.multiple){
19086             ibwrap = {
19087                 tag: 'ul',
19088                 cls: 'roo-select2-choices',
19089                 cn:[
19090                     {
19091                         tag: 'li',
19092                         cls: 'roo-select2-search-field',
19093                         cn: [
19094
19095                             inputblock
19096                         ]
19097                     }
19098                 ]
19099             };
19100         
19101             
19102         }
19103         
19104         var combobox = {
19105             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19106             cn: [
19107                 {
19108                     tag: 'input',
19109                     type : 'hidden',
19110                     cls: 'form-hidden-field'
19111                 },
19112                 ibwrap
19113             ]
19114         };
19115         
19116         if(!this.multiple && this.showToggleBtn){
19117             
19118             var caret = {
19119                 cls: 'caret'
19120             };
19121             
19122             if (this.caret != false) {
19123                 caret = {
19124                      tag: 'i',
19125                      cls: 'fa fa-' + this.caret
19126                 };
19127                 
19128             }
19129             
19130             combobox.cn.push({
19131                 tag :'span',
19132                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19133                 cn : [
19134                     Roo.bootstrap.version == 3 ? caret : '',
19135                     {
19136                         tag: 'span',
19137                         cls: 'combobox-clear',
19138                         cn  : [
19139                             {
19140                                 tag : 'i',
19141                                 cls: 'icon-remove'
19142                             }
19143                         ]
19144                     }
19145                 ]
19146
19147             })
19148         }
19149         
19150         if(this.multiple){
19151             combobox.cls += ' roo-select2-container-multi';
19152         }
19153         
19154         var required =  this.allowBlank ?  {
19155                     tag : 'i',
19156                     style: 'display: none'
19157                 } : {
19158                    tag : 'i',
19159                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19160                    tooltip : 'This field is required'
19161                 };
19162         
19163         var align = this.labelAlign || this.parentLabelAlign();
19164         
19165         if (align ==='left' && this.fieldLabel.length) {
19166
19167             cfg.cn = [
19168                 required,
19169                 {
19170                     tag: 'label',
19171                     cls : 'control-label col-form-label',
19172                     html : this.fieldLabel
19173
19174                 },
19175                 {
19176                     cls : 'roo-combobox-wrap ', 
19177                     cn: [
19178                         combobox
19179                     ]
19180                 }
19181             ];
19182             
19183             var labelCfg = cfg.cn[1];
19184             var contentCfg = cfg.cn[2];
19185             
19186
19187             if(this.indicatorpos == 'right'){
19188                 cfg.cn = [
19189                     {
19190                         tag: 'label',
19191                         'for' :  id,
19192                         cls : 'control-label col-form-label',
19193                         cn : [
19194                             {
19195                                 tag : 'span',
19196                                 html : this.fieldLabel
19197                             },
19198                             required
19199                         ]
19200                     },
19201                     {
19202                         cls : "roo-combobox-wrap ",
19203                         cn: [
19204                             combobox
19205                         ]
19206                     }
19207
19208                 ];
19209                 
19210                 labelCfg = cfg.cn[0];
19211                 contentCfg = cfg.cn[1];
19212             }
19213             
19214            
19215             
19216             if(this.labelWidth > 12){
19217                 labelCfg.style = "width: " + this.labelWidth + 'px';
19218             }
19219            
19220             if(this.labelWidth < 13 && this.labelmd == 0){
19221                 this.labelmd = this.labelWidth;
19222             }
19223             
19224             if(this.labellg > 0){
19225                 labelCfg.cls += ' col-lg-' + this.labellg;
19226                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19227             }
19228             
19229             if(this.labelmd > 0){
19230                 labelCfg.cls += ' col-md-' + this.labelmd;
19231                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19232             }
19233             
19234             if(this.labelsm > 0){
19235                 labelCfg.cls += ' col-sm-' + this.labelsm;
19236                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19237             }
19238             
19239             if(this.labelxs > 0){
19240                 labelCfg.cls += ' col-xs-' + this.labelxs;
19241                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19242             }
19243                 
19244                 
19245         } else if ( this.fieldLabel.length) {
19246             cfg.cn = [
19247                required,
19248                 {
19249                     tag: 'label',
19250                     cls : 'control-label',
19251                     html : this.fieldLabel
19252
19253                 },
19254                 {
19255                     cls : '', 
19256                     cn: [
19257                         combobox
19258                     ]
19259                 }
19260             ];
19261             
19262             if(this.indicatorpos == 'right'){
19263                 cfg.cn = [
19264                     {
19265                         tag: 'label',
19266                         cls : 'control-label',
19267                         html : this.fieldLabel,
19268                         cn : [
19269                             required
19270                         ]
19271                     },
19272                     {
19273                         cls : '', 
19274                         cn: [
19275                             combobox
19276                         ]
19277                     }
19278                 ];
19279             }
19280         } else {
19281             cfg.cn = combobox;    
19282         }
19283         
19284         
19285         var settings = this;
19286         
19287         ['xs','sm','md','lg'].map(function(size){
19288             if (settings[size]) {
19289                 cfg.cls += ' col-' + size + '-' + settings[size];
19290             }
19291         });
19292         
19293         return cfg;
19294     },
19295     
19296     initTouchView : function()
19297     {
19298         this.renderTouchView();
19299         
19300         this.touchViewEl.on('scroll', function(){
19301             this.el.dom.scrollTop = 0;
19302         }, this);
19303         
19304         this.originalValue = this.getValue();
19305         
19306         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19307         
19308         this.inputEl().on("click", this.showTouchView, this);
19309         if (this.triggerEl) {
19310             this.triggerEl.on("click", this.showTouchView, this);
19311         }
19312         
19313         
19314         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19315         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19316         
19317         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19318         
19319         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19320         this.store.on('load', this.onTouchViewLoad, this);
19321         this.store.on('loadexception', this.onTouchViewLoadException, this);
19322         
19323         if(this.hiddenName){
19324             
19325             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19326             
19327             this.hiddenField.dom.value =
19328                 this.hiddenValue !== undefined ? this.hiddenValue :
19329                 this.value !== undefined ? this.value : '';
19330         
19331             this.el.dom.removeAttribute('name');
19332             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19333         }
19334         
19335         if(this.multiple){
19336             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19337             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19338         }
19339         
19340         if(this.removable && !this.multiple){
19341             var close = this.closeTriggerEl();
19342             if(close){
19343                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19344                 close.on('click', this.removeBtnClick, this, close);
19345             }
19346         }
19347         /*
19348          * fix the bug in Safari iOS8
19349          */
19350         this.inputEl().on("focus", function(e){
19351             document.activeElement.blur();
19352         }, this);
19353         
19354         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19355         
19356         return;
19357         
19358         
19359     },
19360     
19361     renderTouchView : function()
19362     {
19363         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19364         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19365         
19366         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19367         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19368         
19369         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19370         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19371         this.touchViewBodyEl.setStyle('overflow', 'auto');
19372         
19373         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19374         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375         
19376         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19377         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19378         
19379     },
19380     
19381     showTouchView : function()
19382     {
19383         if(this.disabled){
19384             return;
19385         }
19386         
19387         this.touchViewHeaderEl.hide();
19388
19389         if(this.modalTitle.length){
19390             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19391             this.touchViewHeaderEl.show();
19392         }
19393
19394         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19395         this.touchViewEl.show();
19396
19397         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19398         
19399         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19400         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19401
19402         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19403
19404         if(this.modalTitle.length){
19405             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19406         }
19407         
19408         this.touchViewBodyEl.setHeight(bodyHeight);
19409
19410         if(this.animate){
19411             var _this = this;
19412             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19413         }else{
19414             this.touchViewEl.addClass(['in','show']);
19415         }
19416         
19417         if(this._touchViewMask){
19418             Roo.get(document.body).addClass("x-body-masked");
19419             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19420             this._touchViewMask.setStyle('z-index', 10000);
19421             this._touchViewMask.addClass('show');
19422         }
19423         
19424         this.doTouchViewQuery();
19425         
19426     },
19427     
19428     hideTouchView : function()
19429     {
19430         this.touchViewEl.removeClass(['in','show']);
19431
19432         if(this.animate){
19433             var _this = this;
19434             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19435         }else{
19436             this.touchViewEl.setStyle('display', 'none');
19437         }
19438         
19439         if(this._touchViewMask){
19440             this._touchViewMask.removeClass('show');
19441             Roo.get(document.body).removeClass("x-body-masked");
19442         }
19443     },
19444     
19445     setTouchViewValue : function()
19446     {
19447         if(this.multiple){
19448             this.clearItem();
19449         
19450             var _this = this;
19451
19452             Roo.each(this.tickItems, function(o){
19453                 this.addItem(o);
19454             }, this);
19455         }
19456         
19457         this.hideTouchView();
19458     },
19459     
19460     doTouchViewQuery : function()
19461     {
19462         var qe = {
19463             query: '',
19464             forceAll: true,
19465             combo: this,
19466             cancel:false
19467         };
19468         
19469         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19470             return false;
19471         }
19472         
19473         if(!this.alwaysQuery || this.mode == 'local'){
19474             this.onTouchViewLoad();
19475             return;
19476         }
19477         
19478         this.store.load();
19479     },
19480     
19481     onTouchViewBeforeLoad : function(combo,opts)
19482     {
19483         return;
19484     },
19485
19486     // private
19487     onTouchViewLoad : function()
19488     {
19489         if(this.store.getCount() < 1){
19490             this.onTouchViewEmptyResults();
19491             return;
19492         }
19493         
19494         this.clearTouchView();
19495         
19496         var rawValue = this.getRawValue();
19497         
19498         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19499         
19500         this.tickItems = [];
19501         
19502         this.store.data.each(function(d, rowIndex){
19503             var row = this.touchViewListGroup.createChild(template);
19504             
19505             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19506                 row.addClass(d.data.cls);
19507             }
19508             
19509             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19510                 var cfg = {
19511                     data : d.data,
19512                     html : d.data[this.displayField]
19513                 };
19514                 
19515                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19516                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19517                 }
19518             }
19519             row.removeClass('selected');
19520             if(!this.multiple && this.valueField &&
19521                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19522             {
19523                 // radio buttons..
19524                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19525                 row.addClass('selected');
19526             }
19527             
19528             if(this.multiple && this.valueField &&
19529                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19530             {
19531                 
19532                 // checkboxes...
19533                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19534                 this.tickItems.push(d.data);
19535             }
19536             
19537             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19538             
19539         }, this);
19540         
19541         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19542         
19543         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19544
19545         if(this.modalTitle.length){
19546             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19547         }
19548
19549         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19550         
19551         if(this.mobile_restrict_height && listHeight < bodyHeight){
19552             this.touchViewBodyEl.setHeight(listHeight);
19553         }
19554         
19555         var _this = this;
19556         
19557         if(firstChecked && listHeight > bodyHeight){
19558             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19559         }
19560         
19561     },
19562     
19563     onTouchViewLoadException : function()
19564     {
19565         this.hideTouchView();
19566     },
19567     
19568     onTouchViewEmptyResults : function()
19569     {
19570         this.clearTouchView();
19571         
19572         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19573         
19574         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19575         
19576     },
19577     
19578     clearTouchView : function()
19579     {
19580         this.touchViewListGroup.dom.innerHTML = '';
19581     },
19582     
19583     onTouchViewClick : function(e, el, o)
19584     {
19585         e.preventDefault();
19586         
19587         var row = o.row;
19588         var rowIndex = o.rowIndex;
19589         
19590         var r = this.store.getAt(rowIndex);
19591         
19592         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19593             
19594             if(!this.multiple){
19595                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19596                     c.dom.removeAttribute('checked');
19597                 }, this);
19598
19599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19600
19601                 this.setFromData(r.data);
19602
19603                 var close = this.closeTriggerEl();
19604
19605                 if(close){
19606                     close.show();
19607                 }
19608
19609                 this.hideTouchView();
19610
19611                 this.fireEvent('select', this, r, rowIndex);
19612
19613                 return;
19614             }
19615
19616             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19617                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19618                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19619                 return;
19620             }
19621
19622             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19623             this.addItem(r.data);
19624             this.tickItems.push(r.data);
19625         }
19626     },
19627     
19628     getAutoCreateNativeIOS : function()
19629     {
19630         var cfg = {
19631             cls: 'form-group' //input-group,
19632         };
19633         
19634         var combobox =  {
19635             tag: 'select',
19636             cls : 'roo-ios-select'
19637         };
19638         
19639         if (this.name) {
19640             combobox.name = this.name;
19641         }
19642         
19643         if (this.disabled) {
19644             combobox.disabled = true;
19645         }
19646         
19647         var settings = this;
19648         
19649         ['xs','sm','md','lg'].map(function(size){
19650             if (settings[size]) {
19651                 cfg.cls += ' col-' + size + '-' + settings[size];
19652             }
19653         });
19654         
19655         cfg.cn = combobox;
19656         
19657         return cfg;
19658         
19659     },
19660     
19661     initIOSView : function()
19662     {
19663         this.store.on('load', this.onIOSViewLoad, this);
19664         
19665         return;
19666     },
19667     
19668     onIOSViewLoad : function()
19669     {
19670         if(this.store.getCount() < 1){
19671             return;
19672         }
19673         
19674         this.clearIOSView();
19675         
19676         if(this.allowBlank) {
19677             
19678             var default_text = '-- SELECT --';
19679             
19680             if(this.placeholder.length){
19681                 default_text = this.placeholder;
19682             }
19683             
19684             if(this.emptyTitle.length){
19685                 default_text += ' - ' + this.emptyTitle + ' -';
19686             }
19687             
19688             var opt = this.inputEl().createChild({
19689                 tag: 'option',
19690                 value : 0,
19691                 html : default_text
19692             });
19693             
19694             var o = {};
19695             o[this.valueField] = 0;
19696             o[this.displayField] = default_text;
19697             
19698             this.ios_options.push({
19699                 data : o,
19700                 el : opt
19701             });
19702             
19703         }
19704         
19705         this.store.data.each(function(d, rowIndex){
19706             
19707             var html = '';
19708             
19709             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19710                 html = d.data[this.displayField];
19711             }
19712             
19713             var value = '';
19714             
19715             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19716                 value = d.data[this.valueField];
19717             }
19718             
19719             var option = {
19720                 tag: 'option',
19721                 value : value,
19722                 html : html
19723             };
19724             
19725             if(this.value == d.data[this.valueField]){
19726                 option['selected'] = true;
19727             }
19728             
19729             var opt = this.inputEl().createChild(option);
19730             
19731             this.ios_options.push({
19732                 data : d.data,
19733                 el : opt
19734             });
19735             
19736         }, this);
19737         
19738         this.inputEl().on('change', function(){
19739            this.fireEvent('select', this);
19740         }, this);
19741         
19742     },
19743     
19744     clearIOSView: function()
19745     {
19746         this.inputEl().dom.innerHTML = '';
19747         
19748         this.ios_options = [];
19749     },
19750     
19751     setIOSValue: function(v)
19752     {
19753         this.value = v;
19754         
19755         if(!this.ios_options){
19756             return;
19757         }
19758         
19759         Roo.each(this.ios_options, function(opts){
19760            
19761            opts.el.dom.removeAttribute('selected');
19762            
19763            if(opts.data[this.valueField] != v){
19764                return;
19765            }
19766            
19767            opts.el.dom.setAttribute('selected', true);
19768            
19769         }, this);
19770     }
19771
19772     /** 
19773     * @cfg {Boolean} grow 
19774     * @hide 
19775     */
19776     /** 
19777     * @cfg {Number} growMin 
19778     * @hide 
19779     */
19780     /** 
19781     * @cfg {Number} growMax 
19782     * @hide 
19783     */
19784     /**
19785      * @hide
19786      * @method autoSize
19787      */
19788 });
19789
19790 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19791     
19792     header : {
19793         tag: 'div',
19794         cls: 'modal-header',
19795         cn: [
19796             {
19797                 tag: 'h4',
19798                 cls: 'modal-title'
19799             }
19800         ]
19801     },
19802     
19803     body : {
19804         tag: 'div',
19805         cls: 'modal-body',
19806         cn: [
19807             {
19808                 tag: 'ul',
19809                 cls: 'list-group'
19810             }
19811         ]
19812     },
19813     
19814     listItemRadio : {
19815         tag: 'li',
19816         cls: 'list-group-item',
19817         cn: [
19818             {
19819                 tag: 'span',
19820                 cls: 'roo-combobox-list-group-item-value'
19821             },
19822             {
19823                 tag: 'div',
19824                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19825                 cn: [
19826                     {
19827                         tag: 'input',
19828                         type: 'radio'
19829                     },
19830                     {
19831                         tag: 'label'
19832                     }
19833                 ]
19834             }
19835         ]
19836     },
19837     
19838     listItemCheckbox : {
19839         tag: 'li',
19840         cls: 'list-group-item',
19841         cn: [
19842             {
19843                 tag: 'span',
19844                 cls: 'roo-combobox-list-group-item-value'
19845             },
19846             {
19847                 tag: 'div',
19848                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19849                 cn: [
19850                     {
19851                         tag: 'input',
19852                         type: 'checkbox'
19853                     },
19854                     {
19855                         tag: 'label'
19856                     }
19857                 ]
19858             }
19859         ]
19860     },
19861     
19862     emptyResult : {
19863         tag: 'div',
19864         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19865     },
19866     
19867     footer : {
19868         tag: 'div',
19869         cls: 'modal-footer',
19870         cn: [
19871             {
19872                 tag: 'div',
19873                 cls: 'row',
19874                 cn: [
19875                     {
19876                         tag: 'div',
19877                         cls: 'col-xs-6 text-left',
19878                         cn: {
19879                             tag: 'button',
19880                             cls: 'btn btn-danger roo-touch-view-cancel',
19881                             html: 'Cancel'
19882                         }
19883                     },
19884                     {
19885                         tag: 'div',
19886                         cls: 'col-xs-6 text-right',
19887                         cn: {
19888                             tag: 'button',
19889                             cls: 'btn btn-success roo-touch-view-ok',
19890                             html: 'OK'
19891                         }
19892                     }
19893                 ]
19894             }
19895         ]
19896         
19897     }
19898 });
19899
19900 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19901     
19902     touchViewTemplate : {
19903         tag: 'div',
19904         cls: 'modal fade roo-combobox-touch-view',
19905         cn: [
19906             {
19907                 tag: 'div',
19908                 cls: 'modal-dialog',
19909                 style : 'position:fixed', // we have to fix position....
19910                 cn: [
19911                     {
19912                         tag: 'div',
19913                         cls: 'modal-content',
19914                         cn: [
19915                             Roo.bootstrap.form.ComboBox.header,
19916                             Roo.bootstrap.form.ComboBox.body,
19917                             Roo.bootstrap.form.ComboBox.footer
19918                         ]
19919                     }
19920                 ]
19921             }
19922         ]
19923     }
19924 });/*
19925  * Based on:
19926  * Ext JS Library 1.1.1
19927  * Copyright(c) 2006-2007, Ext JS, LLC.
19928  *
19929  * Originally Released Under LGPL - original licence link has changed is not relivant.
19930  *
19931  * Fork - LGPL
19932  * <script type="text/javascript">
19933  */
19934
19935 /**
19936  * @class Roo.View
19937  * @extends Roo.util.Observable
19938  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19939  * This class also supports single and multi selection modes. <br>
19940  * Create a data model bound view:
19941  <pre><code>
19942  var store = new Roo.data.Store(...);
19943
19944  var view = new Roo.View({
19945     el : "my-element",
19946     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19947  
19948     singleSelect: true,
19949     selectedClass: "ydataview-selected",
19950     store: store
19951  });
19952
19953  // listen for node click?
19954  view.on("click", function(vw, index, node, e){
19955  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19956  });
19957
19958  // load XML data
19959  dataModel.load("foobar.xml");
19960  </code></pre>
19961  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19962  * <br><br>
19963  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19964  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19965  * 
19966  * Note: old style constructor is still suported (container, template, config)
19967  * 
19968  * @constructor
19969  * Create a new View
19970  * @param {Object} config The config object
19971  * 
19972  */
19973 Roo.View = function(config, depreciated_tpl, depreciated_config){
19974     
19975     this.parent = false;
19976     
19977     if (typeof(depreciated_tpl) == 'undefined') {
19978         // new way.. - universal constructor.
19979         Roo.apply(this, config);
19980         this.el  = Roo.get(this.el);
19981     } else {
19982         // old format..
19983         this.el  = Roo.get(config);
19984         this.tpl = depreciated_tpl;
19985         Roo.apply(this, depreciated_config);
19986     }
19987     this.wrapEl  = this.el.wrap().wrap();
19988     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19989     
19990     
19991     if(typeof(this.tpl) == "string"){
19992         this.tpl = new Roo.Template(this.tpl);
19993     } else {
19994         // support xtype ctors..
19995         this.tpl = new Roo.factory(this.tpl, Roo);
19996     }
19997     
19998     
19999     this.tpl.compile();
20000     
20001     /** @private */
20002     this.addEvents({
20003         /**
20004          * @event beforeclick
20005          * Fires before a click is processed. Returns false to cancel the default action.
20006          * @param {Roo.View} this
20007          * @param {Number} index The index of the target node
20008          * @param {HTMLElement} node The target node
20009          * @param {Roo.EventObject} e The raw event object
20010          */
20011             "beforeclick" : true,
20012         /**
20013          * @event click
20014          * Fires when a template node is clicked.
20015          * @param {Roo.View} this
20016          * @param {Number} index The index of the target node
20017          * @param {HTMLElement} node The target node
20018          * @param {Roo.EventObject} e The raw event object
20019          */
20020             "click" : true,
20021         /**
20022          * @event dblclick
20023          * Fires when a template node is double clicked.
20024          * @param {Roo.View} this
20025          * @param {Number} index The index of the target node
20026          * @param {HTMLElement} node The target node
20027          * @param {Roo.EventObject} e The raw event object
20028          */
20029             "dblclick" : true,
20030         /**
20031          * @event contextmenu
20032          * Fires when a template node is right clicked.
20033          * @param {Roo.View} this
20034          * @param {Number} index The index of the target node
20035          * @param {HTMLElement} node The target node
20036          * @param {Roo.EventObject} e The raw event object
20037          */
20038             "contextmenu" : true,
20039         /**
20040          * @event selectionchange
20041          * Fires when the selected nodes change.
20042          * @param {Roo.View} this
20043          * @param {Array} selections Array of the selected nodes
20044          */
20045             "selectionchange" : true,
20046     
20047         /**
20048          * @event beforeselect
20049          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20050          * @param {Roo.View} this
20051          * @param {HTMLElement} node The node to be selected
20052          * @param {Array} selections Array of currently selected nodes
20053          */
20054             "beforeselect" : true,
20055         /**
20056          * @event preparedata
20057          * Fires on every row to render, to allow you to change the data.
20058          * @param {Roo.View} this
20059          * @param {Object} data to be rendered (change this)
20060          */
20061           "preparedata" : true
20062           
20063           
20064         });
20065
20066
20067
20068     this.el.on({
20069         "click": this.onClick,
20070         "dblclick": this.onDblClick,
20071         "contextmenu": this.onContextMenu,
20072         scope:this
20073     });
20074
20075     this.selections = [];
20076     this.nodes = [];
20077     this.cmp = new Roo.CompositeElementLite([]);
20078     if(this.store){
20079         this.store = Roo.factory(this.store, Roo.data);
20080         this.setStore(this.store, true);
20081     }
20082     
20083     if ( this.footer && this.footer.xtype) {
20084            
20085          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20086         
20087         this.footer.dataSource = this.store;
20088         this.footer.container = fctr;
20089         this.footer = Roo.factory(this.footer, Roo);
20090         fctr.insertFirst(this.el);
20091         
20092         // this is a bit insane - as the paging toolbar seems to detach the el..
20093 //        dom.parentNode.parentNode.parentNode
20094          // they get detached?
20095     }
20096     
20097     
20098     Roo.View.superclass.constructor.call(this);
20099     
20100     
20101 };
20102
20103 Roo.extend(Roo.View, Roo.util.Observable, {
20104     
20105      /**
20106      * @cfg {Roo.data.Store} store Data store to load data from.
20107      */
20108     store : false,
20109     
20110     /**
20111      * @cfg {String|Roo.Element} el The container element.
20112      */
20113     el : '',
20114     
20115     /**
20116      * @cfg {String|Roo.Template} tpl The template used by this View 
20117      */
20118     tpl : false,
20119     /**
20120      * @cfg {String} dataName the named area of the template to use as the data area
20121      *                          Works with domtemplates roo-name="name"
20122      */
20123     dataName: false,
20124     /**
20125      * @cfg {String} selectedClass The css class to add to selected nodes
20126      */
20127     selectedClass : "x-view-selected",
20128      /**
20129      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20130      */
20131     emptyText : "",
20132     
20133     /**
20134      * @cfg {String} text to display on mask (default Loading)
20135      */
20136     mask : false,
20137     /**
20138      * @cfg {Boolean} multiSelect Allow multiple selection
20139      */
20140     multiSelect : false,
20141     /**
20142      * @cfg {Boolean} singleSelect Allow single selection
20143      */
20144     singleSelect:  false,
20145     
20146     /**
20147      * @cfg {Boolean} toggleSelect - selecting 
20148      */
20149     toggleSelect : false,
20150     
20151     /**
20152      * @cfg {Boolean} tickable - selecting 
20153      */
20154     tickable : false,
20155     
20156     /**
20157      * Returns the element this view is bound to.
20158      * @return {Roo.Element}
20159      */
20160     getEl : function(){
20161         return this.wrapEl;
20162     },
20163     
20164     
20165
20166     /**
20167      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20168      */
20169     refresh : function(){
20170         //Roo.log('refresh');
20171         var t = this.tpl;
20172         
20173         // if we are using something like 'domtemplate', then
20174         // the what gets used is:
20175         // t.applySubtemplate(NAME, data, wrapping data..)
20176         // the outer template then get' applied with
20177         //     the store 'extra data'
20178         // and the body get's added to the
20179         //      roo-name="data" node?
20180         //      <span class='roo-tpl-{name}'></span> ?????
20181         
20182         
20183         
20184         this.clearSelections();
20185         this.el.update("");
20186         var html = [];
20187         var records = this.store.getRange();
20188         if(records.length < 1) {
20189             
20190             // is this valid??  = should it render a template??
20191             
20192             this.el.update(this.emptyText);
20193             return;
20194         }
20195         var el = this.el;
20196         if (this.dataName) {
20197             this.el.update(t.apply(this.store.meta)); //????
20198             el = this.el.child('.roo-tpl-' + this.dataName);
20199         }
20200         
20201         for(var i = 0, len = records.length; i < len; i++){
20202             var data = this.prepareData(records[i].data, i, records[i]);
20203             this.fireEvent("preparedata", this, data, i, records[i]);
20204             
20205             var d = Roo.apply({}, data);
20206             
20207             if(this.tickable){
20208                 Roo.apply(d, {'roo-id' : Roo.id()});
20209                 
20210                 var _this = this;
20211             
20212                 Roo.each(this.parent.item, function(item){
20213                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20214                         return;
20215                     }
20216                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20217                 });
20218             }
20219             
20220             html[html.length] = Roo.util.Format.trim(
20221                 this.dataName ?
20222                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20223                     t.apply(d)
20224             );
20225         }
20226         
20227         
20228         
20229         el.update(html.join(""));
20230         this.nodes = el.dom.childNodes;
20231         this.updateIndexes(0);
20232     },
20233     
20234
20235     /**
20236      * Function to override to reformat the data that is sent to
20237      * the template for each node.
20238      * DEPRICATED - use the preparedata event handler.
20239      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20240      * a JSON object for an UpdateManager bound view).
20241      */
20242     prepareData : function(data, index, record)
20243     {
20244         this.fireEvent("preparedata", this, data, index, record);
20245         return data;
20246     },
20247
20248     onUpdate : function(ds, record){
20249         // Roo.log('on update');   
20250         this.clearSelections();
20251         var index = this.store.indexOf(record);
20252         var n = this.nodes[index];
20253         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20254         n.parentNode.removeChild(n);
20255         this.updateIndexes(index, index);
20256     },
20257
20258     
20259     
20260 // --------- FIXME     
20261     onAdd : function(ds, records, index)
20262     {
20263         //Roo.log(['on Add', ds, records, index] );        
20264         this.clearSelections();
20265         if(this.nodes.length == 0){
20266             this.refresh();
20267             return;
20268         }
20269         var n = this.nodes[index];
20270         for(var i = 0, len = records.length; i < len; i++){
20271             var d = this.prepareData(records[i].data, i, records[i]);
20272             if(n){
20273                 this.tpl.insertBefore(n, d);
20274             }else{
20275                 
20276                 this.tpl.append(this.el, d);
20277             }
20278         }
20279         this.updateIndexes(index);
20280     },
20281
20282     onRemove : function(ds, record, index){
20283        // Roo.log('onRemove');
20284         this.clearSelections();
20285         var el = this.dataName  ?
20286             this.el.child('.roo-tpl-' + this.dataName) :
20287             this.el; 
20288         
20289         el.dom.removeChild(this.nodes[index]);
20290         this.updateIndexes(index);
20291     },
20292
20293     /**
20294      * Refresh an individual node.
20295      * @param {Number} index
20296      */
20297     refreshNode : function(index){
20298         this.onUpdate(this.store, this.store.getAt(index));
20299     },
20300
20301     updateIndexes : function(startIndex, endIndex){
20302         var ns = this.nodes;
20303         startIndex = startIndex || 0;
20304         endIndex = endIndex || ns.length - 1;
20305         for(var i = startIndex; i <= endIndex; i++){
20306             ns[i].nodeIndex = i;
20307         }
20308     },
20309
20310     /**
20311      * Changes the data store this view uses and refresh the view.
20312      * @param {Store} store
20313      */
20314     setStore : function(store, initial){
20315         if(!initial && this.store){
20316             this.store.un("datachanged", this.refresh);
20317             this.store.un("add", this.onAdd);
20318             this.store.un("remove", this.onRemove);
20319             this.store.un("update", this.onUpdate);
20320             this.store.un("clear", this.refresh);
20321             this.store.un("beforeload", this.onBeforeLoad);
20322             this.store.un("load", this.onLoad);
20323             this.store.un("loadexception", this.onLoad);
20324         }
20325         if(store){
20326           
20327             store.on("datachanged", this.refresh, this);
20328             store.on("add", this.onAdd, this);
20329             store.on("remove", this.onRemove, this);
20330             store.on("update", this.onUpdate, this);
20331             store.on("clear", this.refresh, this);
20332             store.on("beforeload", this.onBeforeLoad, this);
20333             store.on("load", this.onLoad, this);
20334             store.on("loadexception", this.onLoad, this);
20335         }
20336         
20337         if(store){
20338             this.refresh();
20339         }
20340     },
20341     /**
20342      * onbeforeLoad - masks the loading area.
20343      *
20344      */
20345     onBeforeLoad : function(store,opts)
20346     {
20347          //Roo.log('onBeforeLoad');   
20348         if (!opts.add) {
20349             this.el.update("");
20350         }
20351         this.el.mask(this.mask ? this.mask : "Loading" ); 
20352     },
20353     onLoad : function ()
20354     {
20355         this.el.unmask();
20356     },
20357     
20358
20359     /**
20360      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20361      * @param {HTMLElement} node
20362      * @return {HTMLElement} The template node
20363      */
20364     findItemFromChild : function(node){
20365         var el = this.dataName  ?
20366             this.el.child('.roo-tpl-' + this.dataName,true) :
20367             this.el.dom; 
20368         
20369         if(!node || node.parentNode == el){
20370                     return node;
20371             }
20372             var p = node.parentNode;
20373             while(p && p != el){
20374             if(p.parentNode == el){
20375                 return p;
20376             }
20377             p = p.parentNode;
20378         }
20379             return null;
20380     },
20381
20382     /** @ignore */
20383     onClick : function(e){
20384         var item = this.findItemFromChild(e.getTarget());
20385         if(item){
20386             var index = this.indexOf(item);
20387             if(this.onItemClick(item, index, e) !== false){
20388                 this.fireEvent("click", this, index, item, e);
20389             }
20390         }else{
20391             this.clearSelections();
20392         }
20393     },
20394
20395     /** @ignore */
20396     onContextMenu : function(e){
20397         var item = this.findItemFromChild(e.getTarget());
20398         if(item){
20399             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20400         }
20401     },
20402
20403     /** @ignore */
20404     onDblClick : function(e){
20405         var item = this.findItemFromChild(e.getTarget());
20406         if(item){
20407             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20408         }
20409     },
20410
20411     onItemClick : function(item, index, e)
20412     {
20413         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20414             return false;
20415         }
20416         if (this.toggleSelect) {
20417             var m = this.isSelected(item) ? 'unselect' : 'select';
20418             //Roo.log(m);
20419             var _t = this;
20420             _t[m](item, true, false);
20421             return true;
20422         }
20423         if(this.multiSelect || this.singleSelect){
20424             if(this.multiSelect && e.shiftKey && this.lastSelection){
20425                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20426             }else{
20427                 this.select(item, this.multiSelect && e.ctrlKey);
20428                 this.lastSelection = item;
20429             }
20430             
20431             if(!this.tickable){
20432                 e.preventDefault();
20433             }
20434             
20435         }
20436         return true;
20437     },
20438
20439     /**
20440      * Get the number of selected nodes.
20441      * @return {Number}
20442      */
20443     getSelectionCount : function(){
20444         return this.selections.length;
20445     },
20446
20447     /**
20448      * Get the currently selected nodes.
20449      * @return {Array} An array of HTMLElements
20450      */
20451     getSelectedNodes : function(){
20452         return this.selections;
20453     },
20454
20455     /**
20456      * Get the indexes of the selected nodes.
20457      * @return {Array}
20458      */
20459     getSelectedIndexes : function(){
20460         var indexes = [], s = this.selections;
20461         for(var i = 0, len = s.length; i < len; i++){
20462             indexes.push(s[i].nodeIndex);
20463         }
20464         return indexes;
20465     },
20466
20467     /**
20468      * Clear all selections
20469      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20470      */
20471     clearSelections : function(suppressEvent){
20472         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20473             this.cmp.elements = this.selections;
20474             this.cmp.removeClass(this.selectedClass);
20475             this.selections = [];
20476             if(!suppressEvent){
20477                 this.fireEvent("selectionchange", this, this.selections);
20478             }
20479         }
20480     },
20481
20482     /**
20483      * Returns true if the passed node is selected
20484      * @param {HTMLElement/Number} node The node or node index
20485      * @return {Boolean}
20486      */
20487     isSelected : function(node){
20488         var s = this.selections;
20489         if(s.length < 1){
20490             return false;
20491         }
20492         node = this.getNode(node);
20493         return s.indexOf(node) !== -1;
20494     },
20495
20496     /**
20497      * Selects nodes.
20498      * @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
20499      * @param {Boolean} keepExisting (optional) true to keep existing selections
20500      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20501      */
20502     select : function(nodeInfo, keepExisting, suppressEvent){
20503         if(nodeInfo instanceof Array){
20504             if(!keepExisting){
20505                 this.clearSelections(true);
20506             }
20507             for(var i = 0, len = nodeInfo.length; i < len; i++){
20508                 this.select(nodeInfo[i], true, true);
20509             }
20510             return;
20511         } 
20512         var node = this.getNode(nodeInfo);
20513         if(!node || this.isSelected(node)){
20514             return; // already selected.
20515         }
20516         if(!keepExisting){
20517             this.clearSelections(true);
20518         }
20519         
20520         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20521             Roo.fly(node).addClass(this.selectedClass);
20522             this.selections.push(node);
20523             if(!suppressEvent){
20524                 this.fireEvent("selectionchange", this, this.selections);
20525             }
20526         }
20527         
20528         
20529     },
20530       /**
20531      * Unselects nodes.
20532      * @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
20533      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20534      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20535      */
20536     unselect : function(nodeInfo, keepExisting, suppressEvent)
20537     {
20538         if(nodeInfo instanceof Array){
20539             Roo.each(this.selections, function(s) {
20540                 this.unselect(s, nodeInfo);
20541             }, this);
20542             return;
20543         }
20544         var node = this.getNode(nodeInfo);
20545         if(!node || !this.isSelected(node)){
20546             //Roo.log("not selected");
20547             return; // not selected.
20548         }
20549         // fireevent???
20550         var ns = [];
20551         Roo.each(this.selections, function(s) {
20552             if (s == node ) {
20553                 Roo.fly(node).removeClass(this.selectedClass);
20554
20555                 return;
20556             }
20557             ns.push(s);
20558         },this);
20559         
20560         this.selections= ns;
20561         this.fireEvent("selectionchange", this, this.selections);
20562     },
20563
20564     /**
20565      * Gets a template node.
20566      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20567      * @return {HTMLElement} The node or null if it wasn't found
20568      */
20569     getNode : function(nodeInfo){
20570         if(typeof nodeInfo == "string"){
20571             return document.getElementById(nodeInfo);
20572         }else if(typeof nodeInfo == "number"){
20573             return this.nodes[nodeInfo];
20574         }
20575         return nodeInfo;
20576     },
20577
20578     /**
20579      * Gets a range template nodes.
20580      * @param {Number} startIndex
20581      * @param {Number} endIndex
20582      * @return {Array} An array of nodes
20583      */
20584     getNodes : function(start, end){
20585         var ns = this.nodes;
20586         start = start || 0;
20587         end = typeof end == "undefined" ? ns.length - 1 : end;
20588         var nodes = [];
20589         if(start <= end){
20590             for(var i = start; i <= end; i++){
20591                 nodes.push(ns[i]);
20592             }
20593         } else{
20594             for(var i = start; i >= end; i--){
20595                 nodes.push(ns[i]);
20596             }
20597         }
20598         return nodes;
20599     },
20600
20601     /**
20602      * Finds the index of the passed node
20603      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20604      * @return {Number} The index of the node or -1
20605      */
20606     indexOf : function(node){
20607         node = this.getNode(node);
20608         if(typeof node.nodeIndex == "number"){
20609             return node.nodeIndex;
20610         }
20611         var ns = this.nodes;
20612         for(var i = 0, len = ns.length; i < len; i++){
20613             if(ns[i] == node){
20614                 return i;
20615             }
20616         }
20617         return -1;
20618     }
20619 });
20620 /*
20621  * - LGPL
20622  *
20623  * based on jquery fullcalendar
20624  * 
20625  */
20626
20627 Roo.bootstrap = Roo.bootstrap || {};
20628 /**
20629  * @class Roo.bootstrap.Calendar
20630  * @extends Roo.bootstrap.Component
20631  * Bootstrap Calendar class
20632  * @cfg {Boolean} loadMask (true|false) default false
20633  * @cfg {Object} header generate the user specific header of the calendar, default false
20634
20635  * @constructor
20636  * Create a new Container
20637  * @param {Object} config The config object
20638  */
20639
20640
20641
20642 Roo.bootstrap.Calendar = function(config){
20643     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20644      this.addEvents({
20645         /**
20646              * @event select
20647              * Fires when a date is selected
20648              * @param {DatePicker} this
20649              * @param {Date} date The selected date
20650              */
20651         'select': true,
20652         /**
20653              * @event monthchange
20654              * Fires when the displayed month changes 
20655              * @param {DatePicker} this
20656              * @param {Date} date The selected month
20657              */
20658         'monthchange': true,
20659         /**
20660              * @event evententer
20661              * Fires when mouse over an event
20662              * @param {Calendar} this
20663              * @param {event} Event
20664              */
20665         'evententer': true,
20666         /**
20667              * @event eventleave
20668              * Fires when the mouse leaves an
20669              * @param {Calendar} this
20670              * @param {event}
20671              */
20672         'eventleave': true,
20673         /**
20674              * @event eventclick
20675              * Fires when the mouse click an
20676              * @param {Calendar} this
20677              * @param {event}
20678              */
20679         'eventclick': true
20680         
20681     });
20682
20683 };
20684
20685 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20686     
20687           /**
20688      * @cfg {Roo.data.Store} store
20689      * The data source for the calendar
20690      */
20691         store : false,
20692      /**
20693      * @cfg {Number} startDay
20694      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20695      */
20696     startDay : 0,
20697     
20698     loadMask : false,
20699     
20700     header : false,
20701       
20702     getAutoCreate : function(){
20703         
20704         
20705         var fc_button = function(name, corner, style, content ) {
20706             return Roo.apply({},{
20707                 tag : 'span',
20708                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20709                          (corner.length ?
20710                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20711                             ''
20712                         ),
20713                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20714                 unselectable: 'on'
20715             });
20716         };
20717         
20718         var header = {};
20719         
20720         if(!this.header){
20721             header = {
20722                 tag : 'table',
20723                 cls : 'fc-header',
20724                 style : 'width:100%',
20725                 cn : [
20726                     {
20727                         tag: 'tr',
20728                         cn : [
20729                             {
20730                                 tag : 'td',
20731                                 cls : 'fc-header-left',
20732                                 cn : [
20733                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20734                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20735                                     { tag: 'span', cls: 'fc-header-space' },
20736                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20737
20738
20739                                 ]
20740                             },
20741
20742                             {
20743                                 tag : 'td',
20744                                 cls : 'fc-header-center',
20745                                 cn : [
20746                                     {
20747                                         tag: 'span',
20748                                         cls: 'fc-header-title',
20749                                         cn : {
20750                                             tag: 'H2',
20751                                             html : 'month / year'
20752                                         }
20753                                     }
20754
20755                                 ]
20756                             },
20757                             {
20758                                 tag : 'td',
20759                                 cls : 'fc-header-right',
20760                                 cn : [
20761                               /*      fc_button('month', 'left', '', 'month' ),
20762                                     fc_button('week', '', '', 'week' ),
20763                                     fc_button('day', 'right', '', 'day' )
20764                                 */    
20765
20766                                 ]
20767                             }
20768
20769                         ]
20770                     }
20771                 ]
20772             };
20773         }
20774         
20775         header = this.header;
20776         
20777        
20778         var cal_heads = function() {
20779             var ret = [];
20780             // fixme - handle this.
20781             
20782             for (var i =0; i < Date.dayNames.length; i++) {
20783                 var d = Date.dayNames[i];
20784                 ret.push({
20785                     tag: 'th',
20786                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20787                     html : d.substring(0,3)
20788                 });
20789                 
20790             }
20791             ret[0].cls += ' fc-first';
20792             ret[6].cls += ' fc-last';
20793             return ret;
20794         };
20795         var cal_cell = function(n) {
20796             return  {
20797                 tag: 'td',
20798                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20799                 cn : [
20800                     {
20801                         cn : [
20802                             {
20803                                 cls: 'fc-day-number',
20804                                 html: 'D'
20805                             },
20806                             {
20807                                 cls: 'fc-day-content',
20808                              
20809                                 cn : [
20810                                      {
20811                                         style: 'position: relative;' // height: 17px;
20812                                     }
20813                                 ]
20814                             }
20815                             
20816                             
20817                         ]
20818                     }
20819                 ]
20820                 
20821             }
20822         };
20823         var cal_rows = function() {
20824             
20825             var ret = [];
20826             for (var r = 0; r < 6; r++) {
20827                 var row= {
20828                     tag : 'tr',
20829                     cls : 'fc-week',
20830                     cn : []
20831                 };
20832                 
20833                 for (var i =0; i < Date.dayNames.length; i++) {
20834                     var d = Date.dayNames[i];
20835                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20836
20837                 }
20838                 row.cn[0].cls+=' fc-first';
20839                 row.cn[0].cn[0].style = 'min-height:90px';
20840                 row.cn[6].cls+=' fc-last';
20841                 ret.push(row);
20842                 
20843             }
20844             ret[0].cls += ' fc-first';
20845             ret[4].cls += ' fc-prev-last';
20846             ret[5].cls += ' fc-last';
20847             return ret;
20848             
20849         };
20850         
20851         var cal_table = {
20852             tag: 'table',
20853             cls: 'fc-border-separate',
20854             style : 'width:100%',
20855             cellspacing  : 0,
20856             cn : [
20857                 { 
20858                     tag: 'thead',
20859                     cn : [
20860                         { 
20861                             tag: 'tr',
20862                             cls : 'fc-first fc-last',
20863                             cn : cal_heads()
20864                         }
20865                     ]
20866                 },
20867                 { 
20868                     tag: 'tbody',
20869                     cn : cal_rows()
20870                 }
20871                   
20872             ]
20873         };
20874          
20875          var cfg = {
20876             cls : 'fc fc-ltr',
20877             cn : [
20878                 header,
20879                 {
20880                     cls : 'fc-content',
20881                     style : "position: relative;",
20882                     cn : [
20883                         {
20884                             cls : 'fc-view fc-view-month fc-grid',
20885                             style : 'position: relative',
20886                             unselectable : 'on',
20887                             cn : [
20888                                 {
20889                                     cls : 'fc-event-container',
20890                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20891                                 },
20892                                 cal_table
20893                             ]
20894                         }
20895                     ]
20896     
20897                 }
20898            ] 
20899             
20900         };
20901         
20902          
20903         
20904         return cfg;
20905     },
20906     
20907     
20908     initEvents : function()
20909     {
20910         if(!this.store){
20911             throw "can not find store for calendar";
20912         }
20913         
20914         var mark = {
20915             tag: "div",
20916             cls:"x-dlg-mask",
20917             style: "text-align:center",
20918             cn: [
20919                 {
20920                     tag: "div",
20921                     style: "background-color:white;width:50%;margin:250 auto",
20922                     cn: [
20923                         {
20924                             tag: "img",
20925                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20926                         },
20927                         {
20928                             tag: "span",
20929                             html: "Loading"
20930                         }
20931                         
20932                     ]
20933                 }
20934             ]
20935         };
20936         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20937         
20938         var size = this.el.select('.fc-content', true).first().getSize();
20939         this.maskEl.setSize(size.width, size.height);
20940         this.maskEl.enableDisplayMode("block");
20941         if(!this.loadMask){
20942             this.maskEl.hide();
20943         }
20944         
20945         this.store = Roo.factory(this.store, Roo.data);
20946         this.store.on('load', this.onLoad, this);
20947         this.store.on('beforeload', this.onBeforeLoad, this);
20948         
20949         this.resize();
20950         
20951         this.cells = this.el.select('.fc-day',true);
20952         //Roo.log(this.cells);
20953         this.textNodes = this.el.query('.fc-day-number');
20954         this.cells.addClassOnOver('fc-state-hover');
20955         
20956         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20957         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20958         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20959         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20960         
20961         this.on('monthchange', this.onMonthChange, this);
20962         
20963         this.update(new Date().clearTime());
20964     },
20965     
20966     resize : function() {
20967         var sz  = this.el.getSize();
20968         
20969         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20970         this.el.select('.fc-day-content div',true).setHeight(34);
20971     },
20972     
20973     
20974     // private
20975     showPrevMonth : function(e){
20976         this.update(this.activeDate.add("mo", -1));
20977     },
20978     showToday : function(e){
20979         this.update(new Date().clearTime());
20980     },
20981     // private
20982     showNextMonth : function(e){
20983         this.update(this.activeDate.add("mo", 1));
20984     },
20985
20986     // private
20987     showPrevYear : function(){
20988         this.update(this.activeDate.add("y", -1));
20989     },
20990
20991     // private
20992     showNextYear : function(){
20993         this.update(this.activeDate.add("y", 1));
20994     },
20995
20996     
20997    // private
20998     update : function(date)
20999     {
21000         var vd = this.activeDate;
21001         this.activeDate = date;
21002 //        if(vd && this.el){
21003 //            var t = date.getTime();
21004 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21005 //                Roo.log('using add remove');
21006 //                
21007 //                this.fireEvent('monthchange', this, date);
21008 //                
21009 //                this.cells.removeClass("fc-state-highlight");
21010 //                this.cells.each(function(c){
21011 //                   if(c.dateValue == t){
21012 //                       c.addClass("fc-state-highlight");
21013 //                       setTimeout(function(){
21014 //                            try{c.dom.firstChild.focus();}catch(e){}
21015 //                       }, 50);
21016 //                       return false;
21017 //                   }
21018 //                   return true;
21019 //                });
21020 //                return;
21021 //            }
21022 //        }
21023         
21024         var days = date.getDaysInMonth();
21025         
21026         var firstOfMonth = date.getFirstDateOfMonth();
21027         var startingPos = firstOfMonth.getDay()-this.startDay;
21028         
21029         if(startingPos < this.startDay){
21030             startingPos += 7;
21031         }
21032         
21033         var pm = date.add(Date.MONTH, -1);
21034         var prevStart = pm.getDaysInMonth()-startingPos;
21035 //        
21036         this.cells = this.el.select('.fc-day',true);
21037         this.textNodes = this.el.query('.fc-day-number');
21038         this.cells.addClassOnOver('fc-state-hover');
21039         
21040         var cells = this.cells.elements;
21041         var textEls = this.textNodes;
21042         
21043         Roo.each(cells, function(cell){
21044             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21045         });
21046         
21047         days += startingPos;
21048
21049         // convert everything to numbers so it's fast
21050         var day = 86400000;
21051         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21052         //Roo.log(d);
21053         //Roo.log(pm);
21054         //Roo.log(prevStart);
21055         
21056         var today = new Date().clearTime().getTime();
21057         var sel = date.clearTime().getTime();
21058         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21059         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21060         var ddMatch = this.disabledDatesRE;
21061         var ddText = this.disabledDatesText;
21062         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21063         var ddaysText = this.disabledDaysText;
21064         var format = this.format;
21065         
21066         var setCellClass = function(cal, cell){
21067             cell.row = 0;
21068             cell.events = [];
21069             cell.more = [];
21070             //Roo.log('set Cell Class');
21071             cell.title = "";
21072             var t = d.getTime();
21073             
21074             //Roo.log(d);
21075             
21076             cell.dateValue = t;
21077             if(t == today){
21078                 cell.className += " fc-today";
21079                 cell.className += " fc-state-highlight";
21080                 cell.title = cal.todayText;
21081             }
21082             if(t == sel){
21083                 // disable highlight in other month..
21084                 //cell.className += " fc-state-highlight";
21085                 
21086             }
21087             // disabling
21088             if(t < min) {
21089                 cell.className = " fc-state-disabled";
21090                 cell.title = cal.minText;
21091                 return;
21092             }
21093             if(t > max) {
21094                 cell.className = " fc-state-disabled";
21095                 cell.title = cal.maxText;
21096                 return;
21097             }
21098             if(ddays){
21099                 if(ddays.indexOf(d.getDay()) != -1){
21100                     cell.title = ddaysText;
21101                     cell.className = " fc-state-disabled";
21102                 }
21103             }
21104             if(ddMatch && format){
21105                 var fvalue = d.dateFormat(format);
21106                 if(ddMatch.test(fvalue)){
21107                     cell.title = ddText.replace("%0", fvalue);
21108                     cell.className = " fc-state-disabled";
21109                 }
21110             }
21111             
21112             if (!cell.initialClassName) {
21113                 cell.initialClassName = cell.dom.className;
21114             }
21115             
21116             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21117         };
21118
21119         var i = 0;
21120         
21121         for(; i < startingPos; i++) {
21122             textEls[i].innerHTML = (++prevStart);
21123             d.setDate(d.getDate()+1);
21124             
21125             cells[i].className = "fc-past fc-other-month";
21126             setCellClass(this, cells[i]);
21127         }
21128         
21129         var intDay = 0;
21130         
21131         for(; i < days; i++){
21132             intDay = i - startingPos + 1;
21133             textEls[i].innerHTML = (intDay);
21134             d.setDate(d.getDate()+1);
21135             
21136             cells[i].className = ''; // "x-date-active";
21137             setCellClass(this, cells[i]);
21138         }
21139         var extraDays = 0;
21140         
21141         for(; i < 42; i++) {
21142             textEls[i].innerHTML = (++extraDays);
21143             d.setDate(d.getDate()+1);
21144             
21145             cells[i].className = "fc-future fc-other-month";
21146             setCellClass(this, cells[i]);
21147         }
21148         
21149         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21150         
21151         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21152         
21153         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21154         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21155         
21156         if(totalRows != 6){
21157             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21158             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21159         }
21160         
21161         this.fireEvent('monthchange', this, date);
21162         
21163         
21164         /*
21165         if(!this.internalRender){
21166             var main = this.el.dom.firstChild;
21167             var w = main.offsetWidth;
21168             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21169             Roo.fly(main).setWidth(w);
21170             this.internalRender = true;
21171             // opera does not respect the auto grow header center column
21172             // then, after it gets a width opera refuses to recalculate
21173             // without a second pass
21174             if(Roo.isOpera && !this.secondPass){
21175                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21176                 this.secondPass = true;
21177                 this.update.defer(10, this, [date]);
21178             }
21179         }
21180         */
21181         
21182     },
21183     
21184     findCell : function(dt) {
21185         dt = dt.clearTime().getTime();
21186         var ret = false;
21187         this.cells.each(function(c){
21188             //Roo.log("check " +c.dateValue + '?=' + dt);
21189             if(c.dateValue == dt){
21190                 ret = c;
21191                 return false;
21192             }
21193             return true;
21194         });
21195         
21196         return ret;
21197     },
21198     
21199     findCells : function(ev) {
21200         var s = ev.start.clone().clearTime().getTime();
21201        // Roo.log(s);
21202         var e= ev.end.clone().clearTime().getTime();
21203        // Roo.log(e);
21204         var ret = [];
21205         this.cells.each(function(c){
21206              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21207             
21208             if(c.dateValue > e){
21209                 return ;
21210             }
21211             if(c.dateValue < s){
21212                 return ;
21213             }
21214             ret.push(c);
21215         });
21216         
21217         return ret;    
21218     },
21219     
21220 //    findBestRow: function(cells)
21221 //    {
21222 //        var ret = 0;
21223 //        
21224 //        for (var i =0 ; i < cells.length;i++) {
21225 //            ret  = Math.max(cells[i].rows || 0,ret);
21226 //        }
21227 //        return ret;
21228 //        
21229 //    },
21230     
21231     
21232     addItem : function(ev)
21233     {
21234         // look for vertical location slot in
21235         var cells = this.findCells(ev);
21236         
21237 //        ev.row = this.findBestRow(cells);
21238         
21239         // work out the location.
21240         
21241         var crow = false;
21242         var rows = [];
21243         for(var i =0; i < cells.length; i++) {
21244             
21245             cells[i].row = cells[0].row;
21246             
21247             if(i == 0){
21248                 cells[i].row = cells[i].row + 1;
21249             }
21250             
21251             if (!crow) {
21252                 crow = {
21253                     start : cells[i],
21254                     end :  cells[i]
21255                 };
21256                 continue;
21257             }
21258             if (crow.start.getY() == cells[i].getY()) {
21259                 // on same row.
21260                 crow.end = cells[i];
21261                 continue;
21262             }
21263             // different row.
21264             rows.push(crow);
21265             crow = {
21266                 start: cells[i],
21267                 end : cells[i]
21268             };
21269             
21270         }
21271         
21272         rows.push(crow);
21273         ev.els = [];
21274         ev.rows = rows;
21275         ev.cells = cells;
21276         
21277         cells[0].events.push(ev);
21278         
21279         this.calevents.push(ev);
21280     },
21281     
21282     clearEvents: function() {
21283         
21284         if(!this.calevents){
21285             return;
21286         }
21287         
21288         Roo.each(this.cells.elements, function(c){
21289             c.row = 0;
21290             c.events = [];
21291             c.more = [];
21292         });
21293         
21294         Roo.each(this.calevents, function(e) {
21295             Roo.each(e.els, function(el) {
21296                 el.un('mouseenter' ,this.onEventEnter, this);
21297                 el.un('mouseleave' ,this.onEventLeave, this);
21298                 el.remove();
21299             },this);
21300         },this);
21301         
21302         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21303             e.remove();
21304         });
21305         
21306     },
21307     
21308     renderEvents: function()
21309     {   
21310         var _this = this;
21311         
21312         this.cells.each(function(c) {
21313             
21314             if(c.row < 5){
21315                 return;
21316             }
21317             
21318             var ev = c.events;
21319             
21320             var r = 4;
21321             if(c.row != c.events.length){
21322                 r = 4 - (4 - (c.row - c.events.length));
21323             }
21324             
21325             c.events = ev.slice(0, r);
21326             c.more = ev.slice(r);
21327             
21328             if(c.more.length && c.more.length == 1){
21329                 c.events.push(c.more.pop());
21330             }
21331             
21332             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21333             
21334         });
21335             
21336         this.cells.each(function(c) {
21337             
21338             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21339             
21340             
21341             for (var e = 0; e < c.events.length; e++){
21342                 var ev = c.events[e];
21343                 var rows = ev.rows;
21344                 
21345                 for(var i = 0; i < rows.length; i++) {
21346                 
21347                     // how many rows should it span..
21348
21349                     var  cfg = {
21350                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21351                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21352
21353                         unselectable : "on",
21354                         cn : [
21355                             {
21356                                 cls: 'fc-event-inner',
21357                                 cn : [
21358     //                                {
21359     //                                  tag:'span',
21360     //                                  cls: 'fc-event-time',
21361     //                                  html : cells.length > 1 ? '' : ev.time
21362     //                                },
21363                                     {
21364                                       tag:'span',
21365                                       cls: 'fc-event-title',
21366                                       html : String.format('{0}', ev.title)
21367                                     }
21368
21369
21370                                 ]
21371                             },
21372                             {
21373                                 cls: 'ui-resizable-handle ui-resizable-e',
21374                                 html : '&nbsp;&nbsp;&nbsp'
21375                             }
21376
21377                         ]
21378                     };
21379
21380                     if (i == 0) {
21381                         cfg.cls += ' fc-event-start';
21382                     }
21383                     if ((i+1) == rows.length) {
21384                         cfg.cls += ' fc-event-end';
21385                     }
21386
21387                     var ctr = _this.el.select('.fc-event-container',true).first();
21388                     var cg = ctr.createChild(cfg);
21389
21390                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21391                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21392
21393                     var r = (c.more.length) ? 1 : 0;
21394                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21395                     cg.setWidth(ebox.right - sbox.x -2);
21396
21397                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21398                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21399                     cg.on('click', _this.onEventClick, _this, ev);
21400
21401                     ev.els.push(cg);
21402                     
21403                 }
21404                 
21405             }
21406             
21407             
21408             if(c.more.length){
21409                 var  cfg = {
21410                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21411                     style : 'position: absolute',
21412                     unselectable : "on",
21413                     cn : [
21414                         {
21415                             cls: 'fc-event-inner',
21416                             cn : [
21417                                 {
21418                                   tag:'span',
21419                                   cls: 'fc-event-title',
21420                                   html : 'More'
21421                                 }
21422
21423
21424                             ]
21425                         },
21426                         {
21427                             cls: 'ui-resizable-handle ui-resizable-e',
21428                             html : '&nbsp;&nbsp;&nbsp'
21429                         }
21430
21431                     ]
21432                 };
21433
21434                 var ctr = _this.el.select('.fc-event-container',true).first();
21435                 var cg = ctr.createChild(cfg);
21436
21437                 var sbox = c.select('.fc-day-content',true).first().getBox();
21438                 var ebox = c.select('.fc-day-content',true).first().getBox();
21439                 //Roo.log(cg);
21440                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21441                 cg.setWidth(ebox.right - sbox.x -2);
21442
21443                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21444                 
21445             }
21446             
21447         });
21448         
21449         
21450         
21451     },
21452     
21453     onEventEnter: function (e, el,event,d) {
21454         this.fireEvent('evententer', this, el, event);
21455     },
21456     
21457     onEventLeave: function (e, el,event,d) {
21458         this.fireEvent('eventleave', this, el, event);
21459     },
21460     
21461     onEventClick: function (e, el,event,d) {
21462         this.fireEvent('eventclick', this, el, event);
21463     },
21464     
21465     onMonthChange: function () {
21466         this.store.load();
21467     },
21468     
21469     onMoreEventClick: function(e, el, more)
21470     {
21471         var _this = this;
21472         
21473         this.calpopover.placement = 'right';
21474         this.calpopover.setTitle('More');
21475         
21476         this.calpopover.setContent('');
21477         
21478         var ctr = this.calpopover.el.select('.popover-content', true).first();
21479         
21480         Roo.each(more, function(m){
21481             var cfg = {
21482                 cls : 'fc-event-hori fc-event-draggable',
21483                 html : m.title
21484             };
21485             var cg = ctr.createChild(cfg);
21486             
21487             cg.on('click', _this.onEventClick, _this, m);
21488         });
21489         
21490         this.calpopover.show(el);
21491         
21492         
21493     },
21494     
21495     onLoad: function () 
21496     {   
21497         this.calevents = [];
21498         var cal = this;
21499         
21500         if(this.store.getCount() > 0){
21501             this.store.data.each(function(d){
21502                cal.addItem({
21503                     id : d.data.id,
21504                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21505                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21506                     time : d.data.start_time,
21507                     title : d.data.title,
21508                     description : d.data.description,
21509                     venue : d.data.venue
21510                 });
21511             });
21512         }
21513         
21514         this.renderEvents();
21515         
21516         if(this.calevents.length && this.loadMask){
21517             this.maskEl.hide();
21518         }
21519     },
21520     
21521     onBeforeLoad: function()
21522     {
21523         this.clearEvents();
21524         if(this.loadMask){
21525             this.maskEl.show();
21526         }
21527     }
21528 });
21529
21530  
21531  /*
21532  * - LGPL
21533  *
21534  * element
21535  * 
21536  */
21537
21538 /**
21539  * @class Roo.bootstrap.Popover
21540  * @extends Roo.bootstrap.Component
21541  * @parent none builder
21542  * @children Roo.bootstrap.Component
21543  * Bootstrap Popover class
21544  * @cfg {String} html contents of the popover   (or false to use children..)
21545  * @cfg {String} title of popover (or false to hide)
21546  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21547  * @cfg {String} trigger click || hover (or false to trigger manually)
21548  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21549  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21550  *      - if false and it has a 'parent' then it will be automatically added to that element
21551  *      - if string - Roo.get  will be called 
21552  * @cfg {Number} delay - delay before showing
21553  
21554  * @constructor
21555  * Create a new Popover
21556  * @param {Object} config The config object
21557  */
21558
21559 Roo.bootstrap.Popover = function(config){
21560     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21561     
21562     this.addEvents({
21563         // raw events
21564          /**
21565          * @event show
21566          * After the popover show
21567          * 
21568          * @param {Roo.bootstrap.Popover} this
21569          */
21570         "show" : true,
21571         /**
21572          * @event hide
21573          * After the popover hide
21574          * 
21575          * @param {Roo.bootstrap.Popover} this
21576          */
21577         "hide" : true
21578     });
21579 };
21580
21581 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21582     
21583     title: false,
21584     html: false,
21585     
21586     placement : 'right',
21587     trigger : 'hover', // hover
21588     modal : false,
21589     delay : 0,
21590     
21591     over: false,
21592     
21593     can_build_overlaid : false,
21594     
21595     maskEl : false, // the mask element
21596     headerEl : false,
21597     contentEl : false,
21598     alignEl : false, // when show is called with an element - this get's stored.
21599     
21600     getChildContainer : function()
21601     {
21602         return this.contentEl;
21603         
21604     },
21605     getPopoverHeader : function()
21606     {
21607         this.title = true; // flag not to hide it..
21608         this.headerEl.addClass('p-0');
21609         return this.headerEl
21610     },
21611     
21612     
21613     getAutoCreate : function(){
21614          
21615         var cfg = {
21616            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21617            style: 'display:block',
21618            cn : [
21619                 {
21620                     cls : 'arrow'
21621                 },
21622                 {
21623                     cls : 'popover-inner ',
21624                     cn : [
21625                         {
21626                             tag: 'h3',
21627                             cls: 'popover-title popover-header',
21628                             html : this.title === false ? '' : this.title
21629                         },
21630                         {
21631                             cls : 'popover-content popover-body '  + (this.cls || ''),
21632                             html : this.html || ''
21633                         }
21634                     ]
21635                     
21636                 }
21637            ]
21638         };
21639         
21640         return cfg;
21641     },
21642     /**
21643      * @param {string} the title
21644      */
21645     setTitle: function(str)
21646     {
21647         this.title = str;
21648         if (this.el) {
21649             this.headerEl.dom.innerHTML = str;
21650         }
21651         
21652     },
21653     /**
21654      * @param {string} the body content
21655      */
21656     setContent: function(str)
21657     {
21658         this.html = str;
21659         if (this.contentEl) {
21660             this.contentEl.dom.innerHTML = str;
21661         }
21662         
21663     },
21664     // as it get's added to the bottom of the page.
21665     onRender : function(ct, position)
21666     {
21667         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21668         
21669         
21670         
21671         if(!this.el){
21672             var cfg = Roo.apply({},  this.getAutoCreate());
21673             cfg.id = Roo.id();
21674             
21675             if (this.cls) {
21676                 cfg.cls += ' ' + this.cls;
21677             }
21678             if (this.style) {
21679                 cfg.style = this.style;
21680             }
21681             //Roo.log("adding to ");
21682             this.el = Roo.get(document.body).createChild(cfg, position);
21683 //            Roo.log(this.el);
21684         }
21685         
21686         this.contentEl = this.el.select('.popover-content',true).first();
21687         this.headerEl =  this.el.select('.popover-title',true).first();
21688         
21689         var nitems = [];
21690         if(typeof(this.items) != 'undefined'){
21691             var items = this.items;
21692             delete this.items;
21693
21694             for(var i =0;i < items.length;i++) {
21695                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21696             }
21697         }
21698
21699         this.items = nitems;
21700         
21701         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21702         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21703         
21704         
21705         
21706         this.initEvents();
21707     },
21708     
21709     resizeMask : function()
21710     {
21711         this.maskEl.setSize(
21712             Roo.lib.Dom.getViewWidth(true),
21713             Roo.lib.Dom.getViewHeight(true)
21714         );
21715     },
21716     
21717     initEvents : function()
21718     {
21719         
21720         if (!this.modal) { 
21721             Roo.bootstrap.Popover.register(this);
21722         }
21723          
21724         this.arrowEl = this.el.select('.arrow',true).first();
21725         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21726         this.el.enableDisplayMode('block');
21727         this.el.hide();
21728  
21729         
21730         if (this.over === false && !this.parent()) {
21731             return; 
21732         }
21733         if (this.triggers === false) {
21734             return;
21735         }
21736          
21737         // support parent
21738         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21739         var triggers = this.trigger ? this.trigger.split(' ') : [];
21740         Roo.each(triggers, function(trigger) {
21741         
21742             if (trigger == 'click') {
21743                 on_el.on('click', this.toggle, this);
21744             } else if (trigger != 'manual') {
21745                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21746                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21747       
21748                 on_el.on(eventIn  ,this.enter, this);
21749                 on_el.on(eventOut, this.leave, this);
21750             }
21751         }, this);
21752     },
21753     
21754     
21755     // private
21756     timeout : null,
21757     hoverState : null,
21758     
21759     toggle : function () {
21760         this.hoverState == 'in' ? this.leave() : this.enter();
21761     },
21762     
21763     enter : function () {
21764         
21765         clearTimeout(this.timeout);
21766     
21767         this.hoverState = 'in';
21768     
21769         if (!this.delay || !this.delay.show) {
21770             this.show();
21771             return;
21772         }
21773         var _t = this;
21774         this.timeout = setTimeout(function () {
21775             if (_t.hoverState == 'in') {
21776                 _t.show();
21777             }
21778         }, this.delay.show)
21779     },
21780     
21781     leave : function() {
21782         clearTimeout(this.timeout);
21783     
21784         this.hoverState = 'out';
21785     
21786         if (!this.delay || !this.delay.hide) {
21787             this.hide();
21788             return;
21789         }
21790         var _t = this;
21791         this.timeout = setTimeout(function () {
21792             if (_t.hoverState == 'out') {
21793                 _t.hide();
21794             }
21795         }, this.delay.hide)
21796     },
21797     
21798     /**
21799      * update the position of the dialog
21800      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21801      * 
21802      *
21803      */
21804     
21805     doAlign : function()
21806     {
21807         
21808         if (this.alignEl) {
21809             this.updatePosition(this.placement, true);
21810              
21811         } else {
21812             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21813             var es = this.el.getSize();
21814             var x = Roo.lib.Dom.getViewWidth()/2;
21815             var y = Roo.lib.Dom.getViewHeight()/2;
21816             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21817             
21818         }
21819
21820          
21821          
21822         
21823         
21824     },
21825     
21826     /**
21827      * Show the popover
21828      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21829      * @param {string} (left|right|top|bottom) position
21830      */
21831     show : function (on_el, placement)
21832     {
21833         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21834         on_el = on_el || false; // default to false
21835          
21836         if (!on_el) {
21837             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21838                 on_el = this.parent().el;
21839             } else if (this.over) {
21840                 on_el = Roo.get(this.over);
21841             }
21842             
21843         }
21844         
21845         this.alignEl = Roo.get( on_el );
21846
21847         if (!this.el) {
21848             this.render(document.body);
21849         }
21850         
21851         
21852          
21853         
21854         if (this.title === false) {
21855             this.headerEl.hide();
21856         }
21857         
21858        
21859         this.el.show();
21860         this.el.dom.style.display = 'block';
21861          
21862         this.doAlign();
21863         
21864         //var arrow = this.el.select('.arrow',true).first();
21865         //arrow.set(align[2], 
21866         
21867         this.el.addClass('in');
21868         
21869          
21870         
21871         this.hoverState = 'in';
21872         
21873         if (this.modal) {
21874             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21875             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21876             this.maskEl.dom.style.display = 'block';
21877             this.maskEl.addClass('show');
21878         }
21879         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21880  
21881         this.fireEvent('show', this);
21882         
21883     },
21884     /**
21885      * fire this manually after loading a grid in the table for example
21886      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21887      * @param {Boolean} try and move it if we cant get right position.
21888      */
21889     updatePosition : function(placement, try_move)
21890     {
21891         // allow for calling with no parameters
21892         placement = placement   ? placement :  this.placement;
21893         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21894         
21895         this.el.removeClass([
21896             'fade','top','bottom', 'left', 'right','in',
21897             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21898         ]);
21899         this.el.addClass(placement + ' bs-popover-' + placement);
21900         
21901         if (!this.alignEl ) {
21902             return false;
21903         }
21904         
21905         switch (placement) {
21906             case 'right':
21907                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21908                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21909                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21910                     //normal display... or moved up/down.
21911                     this.el.setXY(offset);
21912                     var xy = this.alignEl.getAnchorXY('tr', false);
21913                     xy[0]+=2;xy[1]+=5;
21914                     this.arrowEl.setXY(xy);
21915                     return true;
21916                 }
21917                 // continue through...
21918                 return this.updatePosition('left', false);
21919                 
21920             
21921             case 'left':
21922                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21923                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21924                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21925                     //normal display... or moved up/down.
21926                     this.el.setXY(offset);
21927                     var xy = this.alignEl.getAnchorXY('tl', false);
21928                     xy[0]-=10;xy[1]+=5; // << fix me
21929                     this.arrowEl.setXY(xy);
21930                     return true;
21931                 }
21932                 // call self...
21933                 return this.updatePosition('right', false);
21934             
21935             case 'top':
21936                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21937                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21938                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21939                     //normal display... or moved up/down.
21940                     this.el.setXY(offset);
21941                     var xy = this.alignEl.getAnchorXY('t', false);
21942                     xy[1]-=10; // << fix me
21943                     this.arrowEl.setXY(xy);
21944                     return true;
21945                 }
21946                 // fall through
21947                return this.updatePosition('bottom', false);
21948             
21949             case 'bottom':
21950                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21951                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21952                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21953                     //normal display... or moved up/down.
21954                     this.el.setXY(offset);
21955                     var xy = this.alignEl.getAnchorXY('b', false);
21956                      xy[1]+=2; // << fix me
21957                     this.arrowEl.setXY(xy);
21958                     return true;
21959                 }
21960                 // fall through
21961                 return this.updatePosition('top', false);
21962                 
21963             
21964         }
21965         
21966         
21967         return false;
21968     },
21969     
21970     hide : function()
21971     {
21972         this.el.setXY([0,0]);
21973         this.el.removeClass('in');
21974         this.el.hide();
21975         this.hoverState = null;
21976         this.maskEl.hide(); // always..
21977         this.fireEvent('hide', this);
21978     }
21979     
21980 });
21981
21982
21983 Roo.apply(Roo.bootstrap.Popover, {
21984
21985     alignment : {
21986         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21987         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21988         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21989         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21990     },
21991     
21992     zIndex : 20001,
21993
21994     clickHander : false,
21995     
21996     
21997
21998     onMouseDown : function(e)
21999     {
22000         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22001             /// what is nothing is showing..
22002             this.hideAll();
22003         }
22004          
22005     },
22006     
22007     
22008     popups : [],
22009     
22010     register : function(popup)
22011     {
22012         if (!Roo.bootstrap.Popover.clickHandler) {
22013             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22014         }
22015         // hide other popups.
22016         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22017         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22018         this.hideAll(); //<< why?
22019         //this.popups.push(popup);
22020     },
22021     hideAll : function()
22022     {
22023         this.popups.forEach(function(p) {
22024             p.hide();
22025         });
22026     },
22027     onShow : function() {
22028         Roo.bootstrap.Popover.popups.push(this);
22029     },
22030     onHide : function() {
22031         Roo.bootstrap.Popover.popups.remove(this);
22032     } 
22033
22034 });
22035 /**
22036  * @class Roo.bootstrap.PopoverNav
22037  * @extends Roo.bootstrap.nav.Simplebar
22038  * @parent Roo.bootstrap.Popover
22039  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22040  * @licence LGPL
22041  * Bootstrap Popover header navigation class
22042  * FIXME? should this go under nav?
22043  *
22044  * 
22045  * @constructor
22046  * Create a new Popover Header Navigation 
22047  * @param {Object} config The config object
22048  */
22049
22050 Roo.bootstrap.PopoverNav = function(config){
22051     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22052 };
22053
22054 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22055     
22056     
22057     container_method : 'getPopoverHeader' 
22058     
22059      
22060     
22061     
22062    
22063 });
22064
22065  
22066
22067  /*
22068  * - LGPL
22069  *
22070  * Progress
22071  * 
22072  */
22073
22074 /**
22075  * @class Roo.bootstrap.Progress
22076  * @extends Roo.bootstrap.Component
22077  * @children Roo.bootstrap.ProgressBar
22078  * Bootstrap Progress class
22079  * @cfg {Boolean} striped striped of the progress bar
22080  * @cfg {Boolean} active animated of the progress bar
22081  * 
22082  * 
22083  * @constructor
22084  * Create a new Progress
22085  * @param {Object} config The config object
22086  */
22087
22088 Roo.bootstrap.Progress = function(config){
22089     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22090 };
22091
22092 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22093     
22094     striped : false,
22095     active: false,
22096     
22097     getAutoCreate : function(){
22098         var cfg = {
22099             tag: 'div',
22100             cls: 'progress'
22101         };
22102         
22103         
22104         if(this.striped){
22105             cfg.cls += ' progress-striped';
22106         }
22107       
22108         if(this.active){
22109             cfg.cls += ' active';
22110         }
22111         
22112         
22113         return cfg;
22114     }
22115    
22116 });
22117
22118  
22119
22120  /*
22121  * - LGPL
22122  *
22123  * ProgressBar
22124  * 
22125  */
22126
22127 /**
22128  * @class Roo.bootstrap.ProgressBar
22129  * @extends Roo.bootstrap.Component
22130  * Bootstrap ProgressBar class
22131  * @cfg {Number} aria_valuenow aria-value now
22132  * @cfg {Number} aria_valuemin aria-value min
22133  * @cfg {Number} aria_valuemax aria-value max
22134  * @cfg {String} label label for the progress bar
22135  * @cfg {String} panel (success | info | warning | danger )
22136  * @cfg {String} role role of the progress bar
22137  * @cfg {String} sr_only text
22138  * 
22139  * 
22140  * @constructor
22141  * Create a new ProgressBar
22142  * @param {Object} config The config object
22143  */
22144
22145 Roo.bootstrap.ProgressBar = function(config){
22146     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22147 };
22148
22149 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22150     
22151     aria_valuenow : 0,
22152     aria_valuemin : 0,
22153     aria_valuemax : 100,
22154     label : false,
22155     panel : false,
22156     role : false,
22157     sr_only: false,
22158     
22159     getAutoCreate : function()
22160     {
22161         
22162         var cfg = {
22163             tag: 'div',
22164             cls: 'progress-bar',
22165             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22166         };
22167         
22168         if(this.sr_only){
22169             cfg.cn = {
22170                 tag: 'span',
22171                 cls: 'sr-only',
22172                 html: this.sr_only
22173             }
22174         }
22175         
22176         if(this.role){
22177             cfg.role = this.role;
22178         }
22179         
22180         if(this.aria_valuenow){
22181             cfg['aria-valuenow'] = this.aria_valuenow;
22182         }
22183         
22184         if(this.aria_valuemin){
22185             cfg['aria-valuemin'] = this.aria_valuemin;
22186         }
22187         
22188         if(this.aria_valuemax){
22189             cfg['aria-valuemax'] = this.aria_valuemax;
22190         }
22191         
22192         if(this.label && !this.sr_only){
22193             cfg.html = this.label;
22194         }
22195         
22196         if(this.panel){
22197             cfg.cls += ' progress-bar-' + this.panel;
22198         }
22199         
22200         return cfg;
22201     },
22202     
22203     update : function(aria_valuenow)
22204     {
22205         this.aria_valuenow = aria_valuenow;
22206         
22207         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22208     }
22209    
22210 });
22211
22212  
22213
22214  /**
22215  * @class Roo.bootstrap.TabGroup
22216  * @extends Roo.bootstrap.Column
22217  * @children Roo.bootstrap.TabPanel
22218  * Bootstrap Column class
22219  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22220  * @cfg {Boolean} carousel true to make the group behave like a carousel
22221  * @cfg {Boolean} bullets show bullets for the panels
22222  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22223  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22224  * @cfg {Boolean} showarrow (true|false) show arrow default true
22225  * 
22226  * @constructor
22227  * Create a new TabGroup
22228  * @param {Object} config The config object
22229  */
22230
22231 Roo.bootstrap.TabGroup = function(config){
22232     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22233     if (!this.navId) {
22234         this.navId = Roo.id();
22235     }
22236     this.tabs = [];
22237     Roo.bootstrap.TabGroup.register(this);
22238     
22239 };
22240
22241 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22242     
22243     carousel : false,
22244     transition : false,
22245     bullets : 0,
22246     timer : 0,
22247     autoslide : false,
22248     slideFn : false,
22249     slideOnTouch : false,
22250     showarrow : true,
22251     
22252     getAutoCreate : function()
22253     {
22254         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22255         
22256         cfg.cls += ' tab-content';
22257         
22258         if (this.carousel) {
22259             cfg.cls += ' carousel slide';
22260             
22261             cfg.cn = [{
22262                cls : 'carousel-inner',
22263                cn : []
22264             }];
22265         
22266             if(this.bullets  && !Roo.isTouch){
22267                 
22268                 var bullets = {
22269                     cls : 'carousel-bullets',
22270                     cn : []
22271                 };
22272                
22273                 if(this.bullets_cls){
22274                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22275                 }
22276                 
22277                 bullets.cn.push({
22278                     cls : 'clear'
22279                 });
22280                 
22281                 cfg.cn[0].cn.push(bullets);
22282             }
22283             
22284             if(this.showarrow){
22285                 cfg.cn[0].cn.push({
22286                     tag : 'div',
22287                     class : 'carousel-arrow',
22288                     cn : [
22289                         {
22290                             tag : 'div',
22291                             class : 'carousel-prev',
22292                             cn : [
22293                                 {
22294                                     tag : 'i',
22295                                     class : 'fa fa-chevron-left'
22296                                 }
22297                             ]
22298                         },
22299                         {
22300                             tag : 'div',
22301                             class : 'carousel-next',
22302                             cn : [
22303                                 {
22304                                     tag : 'i',
22305                                     class : 'fa fa-chevron-right'
22306                                 }
22307                             ]
22308                         }
22309                     ]
22310                 });
22311             }
22312             
22313         }
22314         
22315         return cfg;
22316     },
22317     
22318     initEvents:  function()
22319     {
22320 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22321 //            this.el.on("touchstart", this.onTouchStart, this);
22322 //        }
22323         
22324         if(this.autoslide){
22325             var _this = this;
22326             
22327             this.slideFn = window.setInterval(function() {
22328                 _this.showPanelNext();
22329             }, this.timer);
22330         }
22331         
22332         if(this.showarrow){
22333             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22334             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22335         }
22336         
22337         
22338     },
22339     
22340 //    onTouchStart : function(e, el, o)
22341 //    {
22342 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22343 //            return;
22344 //        }
22345 //        
22346 //        this.showPanelNext();
22347 //    },
22348     
22349     
22350     getChildContainer : function()
22351     {
22352         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22353     },
22354     
22355     /**
22356     * register a Navigation item
22357     * @param {Roo.bootstrap.nav.Item} the navitem to add
22358     */
22359     register : function(item)
22360     {
22361         this.tabs.push( item);
22362         item.navId = this.navId; // not really needed..
22363         this.addBullet();
22364     
22365     },
22366     
22367     getActivePanel : function()
22368     {
22369         var r = false;
22370         Roo.each(this.tabs, function(t) {
22371             if (t.active) {
22372                 r = t;
22373                 return false;
22374             }
22375             return null;
22376         });
22377         return r;
22378         
22379     },
22380     getPanelByName : function(n)
22381     {
22382         var r = false;
22383         Roo.each(this.tabs, function(t) {
22384             if (t.tabId == n) {
22385                 r = t;
22386                 return false;
22387             }
22388             return null;
22389         });
22390         return r;
22391     },
22392     indexOfPanel : function(p)
22393     {
22394         var r = false;
22395         Roo.each(this.tabs, function(t,i) {
22396             if (t.tabId == p.tabId) {
22397                 r = i;
22398                 return false;
22399             }
22400             return null;
22401         });
22402         return r;
22403     },
22404     /**
22405      * show a specific panel
22406      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22407      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22408      */
22409     showPanel : function (pan)
22410     {
22411         if(this.transition || typeof(pan) == 'undefined'){
22412             Roo.log("waiting for the transitionend");
22413             return false;
22414         }
22415         
22416         if (typeof(pan) == 'number') {
22417             pan = this.tabs[pan];
22418         }
22419         
22420         if (typeof(pan) == 'string') {
22421             pan = this.getPanelByName(pan);
22422         }
22423         
22424         var cur = this.getActivePanel();
22425         
22426         if(!pan || !cur){
22427             Roo.log('pan or acitve pan is undefined');
22428             return false;
22429         }
22430         
22431         if (pan.tabId == this.getActivePanel().tabId) {
22432             return true;
22433         }
22434         
22435         if (false === cur.fireEvent('beforedeactivate')) {
22436             return false;
22437         }
22438         
22439         if(this.bullets > 0 && !Roo.isTouch){
22440             this.setActiveBullet(this.indexOfPanel(pan));
22441         }
22442         
22443         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22444             
22445             //class="carousel-item carousel-item-next carousel-item-left"
22446             
22447             this.transition = true;
22448             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22449             var lr = dir == 'next' ? 'left' : 'right';
22450             pan.el.addClass(dir); // or prev
22451             pan.el.addClass('carousel-item-' + dir); // or prev
22452             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22453             cur.el.addClass(lr); // or right
22454             pan.el.addClass(lr);
22455             cur.el.addClass('carousel-item-' +lr); // or right
22456             pan.el.addClass('carousel-item-' +lr);
22457             
22458             
22459             var _this = this;
22460             cur.el.on('transitionend', function() {
22461                 Roo.log("trans end?");
22462                 
22463                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22464                 pan.setActive(true);
22465                 
22466                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22467                 cur.setActive(false);
22468                 
22469                 _this.transition = false;
22470                 
22471             }, this, { single:  true } );
22472             
22473             return true;
22474         }
22475         
22476         cur.setActive(false);
22477         pan.setActive(true);
22478         
22479         return true;
22480         
22481     },
22482     showPanelNext : function()
22483     {
22484         var i = this.indexOfPanel(this.getActivePanel());
22485         
22486         if (i >= this.tabs.length - 1 && !this.autoslide) {
22487             return;
22488         }
22489         
22490         if (i >= this.tabs.length - 1 && this.autoslide) {
22491             i = -1;
22492         }
22493         
22494         this.showPanel(this.tabs[i+1]);
22495     },
22496     
22497     showPanelPrev : function()
22498     {
22499         var i = this.indexOfPanel(this.getActivePanel());
22500         
22501         if (i  < 1 && !this.autoslide) {
22502             return;
22503         }
22504         
22505         if (i < 1 && this.autoslide) {
22506             i = this.tabs.length;
22507         }
22508         
22509         this.showPanel(this.tabs[i-1]);
22510     },
22511     
22512     
22513     addBullet: function()
22514     {
22515         if(!this.bullets || Roo.isTouch){
22516             return;
22517         }
22518         var ctr = this.el.select('.carousel-bullets',true).first();
22519         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22520         var bullet = ctr.createChild({
22521             cls : 'bullet bullet-' + i
22522         },ctr.dom.lastChild);
22523         
22524         
22525         var _this = this;
22526         
22527         bullet.on('click', (function(e, el, o, ii, t){
22528
22529             e.preventDefault();
22530
22531             this.showPanel(ii);
22532
22533             if(this.autoslide && this.slideFn){
22534                 clearInterval(this.slideFn);
22535                 this.slideFn = window.setInterval(function() {
22536                     _this.showPanelNext();
22537                 }, this.timer);
22538             }
22539
22540         }).createDelegate(this, [i, bullet], true));
22541                 
22542         
22543     },
22544      
22545     setActiveBullet : function(i)
22546     {
22547         if(Roo.isTouch){
22548             return;
22549         }
22550         
22551         Roo.each(this.el.select('.bullet', true).elements, function(el){
22552             el.removeClass('selected');
22553         });
22554
22555         var bullet = this.el.select('.bullet-' + i, true).first();
22556         
22557         if(!bullet){
22558             return;
22559         }
22560         
22561         bullet.addClass('selected');
22562     }
22563     
22564     
22565   
22566 });
22567
22568  
22569
22570  
22571  
22572 Roo.apply(Roo.bootstrap.TabGroup, {
22573     
22574     groups: {},
22575      /**
22576     * register a Navigation Group
22577     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22578     */
22579     register : function(navgrp)
22580     {
22581         this.groups[navgrp.navId] = navgrp;
22582         
22583     },
22584     /**
22585     * fetch a Navigation Group based on the navigation ID
22586     * if one does not exist , it will get created.
22587     * @param {string} the navgroup to add
22588     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22589     */
22590     get: function(navId) {
22591         if (typeof(this.groups[navId]) == 'undefined') {
22592             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22593         }
22594         return this.groups[navId] ;
22595     }
22596     
22597     
22598     
22599 });
22600
22601  /*
22602  * - LGPL
22603  *
22604  * TabPanel
22605  * 
22606  */
22607
22608 /**
22609  * @class Roo.bootstrap.TabPanel
22610  * @extends Roo.bootstrap.Component
22611  * @children Roo.bootstrap.Component
22612  * Bootstrap TabPanel class
22613  * @cfg {Boolean} active panel active
22614  * @cfg {String} html panel content
22615  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22616  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22617  * @cfg {String} href click to link..
22618  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22619  * 
22620  * 
22621  * @constructor
22622  * Create a new TabPanel
22623  * @param {Object} config The config object
22624  */
22625
22626 Roo.bootstrap.TabPanel = function(config){
22627     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22628     this.addEvents({
22629         /**
22630              * @event changed
22631              * Fires when the active status changes
22632              * @param {Roo.bootstrap.TabPanel} this
22633              * @param {Boolean} state the new state
22634             
22635          */
22636         'changed': true,
22637         /**
22638              * @event beforedeactivate
22639              * Fires before a tab is de-activated - can be used to do validation on a form.
22640              * @param {Roo.bootstrap.TabPanel} this
22641              * @return {Boolean} false if there is an error
22642             
22643          */
22644         'beforedeactivate': true
22645      });
22646     
22647     this.tabId = this.tabId || Roo.id();
22648   
22649 };
22650
22651 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22652     
22653     active: false,
22654     html: false,
22655     tabId: false,
22656     navId : false,
22657     href : '',
22658     touchSlide : false,
22659     getAutoCreate : function(){
22660         
22661         
22662         var cfg = {
22663             tag: 'div',
22664             // item is needed for carousel - not sure if it has any effect otherwise
22665             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22666             html: this.html || ''
22667         };
22668         
22669         if(this.active){
22670             cfg.cls += ' active';
22671         }
22672         
22673         if(this.tabId){
22674             cfg.tabId = this.tabId;
22675         }
22676         
22677         
22678         
22679         return cfg;
22680     },
22681     
22682     initEvents:  function()
22683     {
22684         var p = this.parent();
22685         
22686         this.navId = this.navId || p.navId;
22687         
22688         if (typeof(this.navId) != 'undefined') {
22689             // not really needed.. but just in case.. parent should be a NavGroup.
22690             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22691             
22692             tg.register(this);
22693             
22694             var i = tg.tabs.length - 1;
22695             
22696             if(this.active && tg.bullets > 0 && i < tg.bullets){
22697                 tg.setActiveBullet(i);
22698             }
22699         }
22700         
22701         this.el.on('click', this.onClick, this);
22702         
22703         if(Roo.isTouch && this.touchSlide){
22704             this.el.on("touchstart", this.onTouchStart, this);
22705             this.el.on("touchmove", this.onTouchMove, this);
22706             this.el.on("touchend", this.onTouchEnd, this);
22707         }
22708         
22709     },
22710     
22711     onRender : function(ct, position)
22712     {
22713         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22714     },
22715     
22716     setActive : function(state)
22717     {
22718         Roo.log("panel - set active " + this.tabId + "=" + state);
22719         
22720         this.active = state;
22721         if (!state) {
22722             this.el.removeClass('active');
22723             
22724         } else  if (!this.el.hasClass('active')) {
22725             this.el.addClass('active');
22726         }
22727         
22728         this.fireEvent('changed', this, state);
22729     },
22730     
22731     onClick : function(e)
22732     {
22733         e.preventDefault();
22734         
22735         if(!this.href.length){
22736             return;
22737         }
22738         
22739         window.location.href = this.href;
22740     },
22741     
22742     startX : 0,
22743     startY : 0,
22744     endX : 0,
22745     endY : 0,
22746     swiping : false,
22747     
22748     onTouchStart : function(e)
22749     {
22750         this.swiping = false;
22751         
22752         this.startX = e.browserEvent.touches[0].clientX;
22753         this.startY = e.browserEvent.touches[0].clientY;
22754     },
22755     
22756     onTouchMove : function(e)
22757     {
22758         this.swiping = true;
22759         
22760         this.endX = e.browserEvent.touches[0].clientX;
22761         this.endY = e.browserEvent.touches[0].clientY;
22762     },
22763     
22764     onTouchEnd : function(e)
22765     {
22766         if(!this.swiping){
22767             this.onClick(e);
22768             return;
22769         }
22770         
22771         var tabGroup = this.parent();
22772         
22773         if(this.endX > this.startX){ // swiping right
22774             tabGroup.showPanelPrev();
22775             return;
22776         }
22777         
22778         if(this.startX > this.endX){ // swiping left
22779             tabGroup.showPanelNext();
22780             return;
22781         }
22782     }
22783     
22784     
22785 });
22786  
22787
22788  
22789
22790  /*
22791  * - LGPL
22792  *
22793  * DateField
22794  * 
22795  */
22796
22797 /**
22798  * @class Roo.bootstrap.form.DateField
22799  * @extends Roo.bootstrap.form.Input
22800  * Bootstrap DateField class
22801  * @cfg {Number} weekStart default 0
22802  * @cfg {String} viewMode default empty, (months|years)
22803  * @cfg {String} minViewMode default empty, (months|years)
22804  * @cfg {Number} startDate default -Infinity
22805  * @cfg {Number} endDate default Infinity
22806  * @cfg {Boolean} todayHighlight default false
22807  * @cfg {Boolean} todayBtn default false
22808  * @cfg {Boolean} calendarWeeks default false
22809  * @cfg {Object} daysOfWeekDisabled default empty
22810  * @cfg {Boolean} singleMode default false (true | false)
22811  * 
22812  * @cfg {Boolean} keyboardNavigation default true
22813  * @cfg {String} language default en
22814  * 
22815  * @constructor
22816  * Create a new DateField
22817  * @param {Object} config The config object
22818  */
22819
22820 Roo.bootstrap.form.DateField = function(config){
22821     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22822      this.addEvents({
22823             /**
22824              * @event show
22825              * Fires when this field show.
22826              * @param {Roo.bootstrap.form.DateField} this
22827              * @param {Mixed} date The date value
22828              */
22829             show : true,
22830             /**
22831              * @event show
22832              * Fires when this field hide.
22833              * @param {Roo.bootstrap.form.DateField} this
22834              * @param {Mixed} date The date value
22835              */
22836             hide : true,
22837             /**
22838              * @event select
22839              * Fires when select a date.
22840              * @param {Roo.bootstrap.form.DateField} this
22841              * @param {Mixed} date The date value
22842              */
22843             select : true,
22844             /**
22845              * @event beforeselect
22846              * Fires when before select a date.
22847              * @param {Roo.bootstrap.form.DateField} this
22848              * @param {Mixed} date The date value
22849              */
22850             beforeselect : true
22851         });
22852 };
22853
22854 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22855     
22856     /**
22857      * @cfg {String} format
22858      * The default date format string which can be overriden for localization support.  The format must be
22859      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22860      */
22861     format : "m/d/y",
22862     /**
22863      * @cfg {String} altFormats
22864      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22865      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22866      */
22867     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22868     
22869     weekStart : 0,
22870     
22871     viewMode : '',
22872     
22873     minViewMode : '',
22874     
22875     todayHighlight : false,
22876     
22877     todayBtn: false,
22878     
22879     language: 'en',
22880     
22881     keyboardNavigation: true,
22882     
22883     calendarWeeks: false,
22884     
22885     startDate: -Infinity,
22886     
22887     endDate: Infinity,
22888     
22889     daysOfWeekDisabled: [],
22890     
22891     _events: [],
22892     
22893     singleMode : false,
22894     
22895     UTCDate: function()
22896     {
22897         return new Date(Date.UTC.apply(Date, arguments));
22898     },
22899     
22900     UTCToday: function()
22901     {
22902         var today = new Date();
22903         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22904     },
22905     
22906     getDate: function() {
22907             var d = this.getUTCDate();
22908             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22909     },
22910     
22911     getUTCDate: function() {
22912             return this.date;
22913     },
22914     
22915     setDate: function(d) {
22916             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22917     },
22918     
22919     setUTCDate: function(d) {
22920             this.date = d;
22921             this.setValue(this.formatDate(this.date));
22922     },
22923         
22924     onRender: function(ct, position)
22925     {
22926         
22927         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22928         
22929         this.language = this.language || 'en';
22930         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22931         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22932         
22933         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22934         this.format = this.format || 'm/d/y';
22935         this.isInline = false;
22936         this.isInput = true;
22937         this.component = this.el.select('.add-on', true).first() || false;
22938         this.component = (this.component && this.component.length === 0) ? false : this.component;
22939         this.hasInput = this.component && this.inputEl().length;
22940         
22941         if (typeof(this.minViewMode === 'string')) {
22942             switch (this.minViewMode) {
22943                 case 'months':
22944                     this.minViewMode = 1;
22945                     break;
22946                 case 'years':
22947                     this.minViewMode = 2;
22948                     break;
22949                 default:
22950                     this.minViewMode = 0;
22951                     break;
22952             }
22953         }
22954         
22955         if (typeof(this.viewMode === 'string')) {
22956             switch (this.viewMode) {
22957                 case 'months':
22958                     this.viewMode = 1;
22959                     break;
22960                 case 'years':
22961                     this.viewMode = 2;
22962                     break;
22963                 default:
22964                     this.viewMode = 0;
22965                     break;
22966             }
22967         }
22968                 
22969         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22970         
22971 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22972         
22973         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22974         
22975         this.picker().on('mousedown', this.onMousedown, this);
22976         this.picker().on('click', this.onClick, this);
22977         
22978         this.picker().addClass('datepicker-dropdown');
22979         
22980         this.startViewMode = this.viewMode;
22981         
22982         if(this.singleMode){
22983             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22984                 v.setVisibilityMode(Roo.Element.DISPLAY);
22985                 v.hide();
22986             });
22987             
22988             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22989                 v.setStyle('width', '189px');
22990             });
22991         }
22992         
22993         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22994             if(!this.calendarWeeks){
22995                 v.remove();
22996                 return;
22997             }
22998             
22999             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23000             v.attr('colspan', function(i, val){
23001                 return parseInt(val) + 1;
23002             });
23003         });
23004                         
23005         
23006         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23007         
23008         this.setStartDate(this.startDate);
23009         this.setEndDate(this.endDate);
23010         
23011         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23012         
23013         this.fillDow();
23014         this.fillMonths();
23015         this.update();
23016         this.showMode();
23017         
23018         if(this.isInline) {
23019             this.showPopup();
23020         }
23021     },
23022     
23023     picker : function()
23024     {
23025         return this.pickerEl;
23026 //        return this.el.select('.datepicker', true).first();
23027     },
23028     
23029     fillDow: function()
23030     {
23031         var dowCnt = this.weekStart;
23032         
23033         var dow = {
23034             tag: 'tr',
23035             cn: [
23036                 
23037             ]
23038         };
23039         
23040         if(this.calendarWeeks){
23041             dow.cn.push({
23042                 tag: 'th',
23043                 cls: 'cw',
23044                 html: '&nbsp;'
23045             })
23046         }
23047         
23048         while (dowCnt < this.weekStart + 7) {
23049             dow.cn.push({
23050                 tag: 'th',
23051                 cls: 'dow',
23052                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23053             });
23054         }
23055         
23056         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23057     },
23058     
23059     fillMonths: function()
23060     {    
23061         var i = 0;
23062         var months = this.picker().select('>.datepicker-months td', true).first();
23063         
23064         months.dom.innerHTML = '';
23065         
23066         while (i < 12) {
23067             var month = {
23068                 tag: 'span',
23069                 cls: 'month',
23070                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23071             };
23072             
23073             months.createChild(month);
23074         }
23075         
23076     },
23077     
23078     update: function()
23079     {
23080         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;
23081         
23082         if (this.date < this.startDate) {
23083             this.viewDate = new Date(this.startDate);
23084         } else if (this.date > this.endDate) {
23085             this.viewDate = new Date(this.endDate);
23086         } else {
23087             this.viewDate = new Date(this.date);
23088         }
23089         
23090         this.fill();
23091     },
23092     
23093     fill: function() 
23094     {
23095         var d = new Date(this.viewDate),
23096                 year = d.getUTCFullYear(),
23097                 month = d.getUTCMonth(),
23098                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23099                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23100                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23101                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23102                 currentDate = this.date && this.date.valueOf(),
23103                 today = this.UTCToday();
23104         
23105         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23106         
23107 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23108         
23109 //        this.picker.select('>tfoot th.today').
23110 //                                              .text(dates[this.language].today)
23111 //                                              .toggle(this.todayBtn !== false);
23112     
23113         this.updateNavArrows();
23114         this.fillMonths();
23115                                                 
23116         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23117         
23118         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23119          
23120         prevMonth.setUTCDate(day);
23121         
23122         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23123         
23124         var nextMonth = new Date(prevMonth);
23125         
23126         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23127         
23128         nextMonth = nextMonth.valueOf();
23129         
23130         var fillMonths = false;
23131         
23132         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23133         
23134         while(prevMonth.valueOf() <= nextMonth) {
23135             var clsName = '';
23136             
23137             if (prevMonth.getUTCDay() === this.weekStart) {
23138                 if(fillMonths){
23139                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23140                 }
23141                     
23142                 fillMonths = {
23143                     tag: 'tr',
23144                     cn: []
23145                 };
23146                 
23147                 if(this.calendarWeeks){
23148                     // ISO 8601: First week contains first thursday.
23149                     // ISO also states week starts on Monday, but we can be more abstract here.
23150                     var
23151                     // Start of current week: based on weekstart/current date
23152                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23153                     // Thursday of this week
23154                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23155                     // First Thursday of year, year from thursday
23156                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23157                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23158                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23159                     
23160                     fillMonths.cn.push({
23161                         tag: 'td',
23162                         cls: 'cw',
23163                         html: calWeek
23164                     });
23165                 }
23166             }
23167             
23168             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23169                 clsName += ' old';
23170             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23171                 clsName += ' new';
23172             }
23173             if (this.todayHighlight &&
23174                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23175                 prevMonth.getUTCMonth() == today.getMonth() &&
23176                 prevMonth.getUTCDate() == today.getDate()) {
23177                 clsName += ' today';
23178             }
23179             
23180             if (currentDate && prevMonth.valueOf() === currentDate) {
23181                 clsName += ' active';
23182             }
23183             
23184             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23185                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23186                     clsName += ' disabled';
23187             }
23188             
23189             fillMonths.cn.push({
23190                 tag: 'td',
23191                 cls: 'day ' + clsName,
23192                 html: prevMonth.getDate()
23193             });
23194             
23195             prevMonth.setDate(prevMonth.getDate()+1);
23196         }
23197           
23198         var currentYear = this.date && this.date.getUTCFullYear();
23199         var currentMonth = this.date && this.date.getUTCMonth();
23200         
23201         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23202         
23203         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23204             v.removeClass('active');
23205             
23206             if(currentYear === year && k === currentMonth){
23207                 v.addClass('active');
23208             }
23209             
23210             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23211                 v.addClass('disabled');
23212             }
23213             
23214         });
23215         
23216         
23217         year = parseInt(year/10, 10) * 10;
23218         
23219         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23220         
23221         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23222         
23223         year -= 1;
23224         for (var i = -1; i < 11; i++) {
23225             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23226                 tag: 'span',
23227                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23228                 html: year
23229             });
23230             
23231             year += 1;
23232         }
23233     },
23234     
23235     showMode: function(dir) 
23236     {
23237         if (dir) {
23238             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23239         }
23240         
23241         Roo.each(this.picker().select('>div',true).elements, function(v){
23242             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23243             v.hide();
23244         });
23245         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23246     },
23247     
23248     place: function()
23249     {
23250         if(this.isInline) {
23251             return;
23252         }
23253         
23254         this.picker().removeClass(['bottom', 'top']);
23255         
23256         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23257             /*
23258              * place to the top of element!
23259              *
23260              */
23261             
23262             this.picker().addClass('top');
23263             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23264             
23265             return;
23266         }
23267         
23268         this.picker().addClass('bottom');
23269         
23270         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23271     },
23272     
23273     parseDate : function(value)
23274     {
23275         if(!value || value instanceof Date){
23276             return value;
23277         }
23278         var v = Date.parseDate(value, this.format);
23279         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23280             v = Date.parseDate(value, 'Y-m-d');
23281         }
23282         if(!v && this.altFormats){
23283             if(!this.altFormatsArray){
23284                 this.altFormatsArray = this.altFormats.split("|");
23285             }
23286             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23287                 v = Date.parseDate(value, this.altFormatsArray[i]);
23288             }
23289         }
23290         return v;
23291     },
23292     
23293     formatDate : function(date, fmt)
23294     {   
23295         return (!date || !(date instanceof Date)) ?
23296         date : date.dateFormat(fmt || this.format);
23297     },
23298     
23299     onFocus : function()
23300     {
23301         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23302         this.showPopup();
23303     },
23304     
23305     onBlur : function()
23306     {
23307         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23308         
23309         var d = this.inputEl().getValue();
23310         
23311         this.setValue(d);
23312                 
23313         this.hidePopup();
23314     },
23315     
23316     showPopup : function()
23317     {
23318         this.picker().show();
23319         this.update();
23320         this.place();
23321         
23322         this.fireEvent('showpopup', this, this.date);
23323     },
23324     
23325     hidePopup : function()
23326     {
23327         if(this.isInline) {
23328             return;
23329         }
23330         this.picker().hide();
23331         this.viewMode = this.startViewMode;
23332         this.showMode();
23333         
23334         this.fireEvent('hidepopup', this, this.date);
23335         
23336     },
23337     
23338     onMousedown: function(e)
23339     {
23340         e.stopPropagation();
23341         e.preventDefault();
23342     },
23343     
23344     keyup: function(e)
23345     {
23346         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23347         this.update();
23348     },
23349
23350     setValue: function(v)
23351     {
23352         if(this.fireEvent('beforeselect', this, v) !== false){
23353             var d = new Date(this.parseDate(v) ).clearTime();
23354         
23355             if(isNaN(d.getTime())){
23356                 this.date = this.viewDate = '';
23357                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23358                 return;
23359             }
23360
23361             v = this.formatDate(d);
23362
23363             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23364
23365             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23366
23367             this.update();
23368
23369             this.fireEvent('select', this, this.date);
23370         }
23371     },
23372     
23373     getValue: function()
23374     {
23375         return this.formatDate(this.date);
23376     },
23377     
23378     fireKey: function(e)
23379     {
23380         if (!this.picker().isVisible()){
23381             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23382                 this.showPopup();
23383             }
23384             return;
23385         }
23386         
23387         var dateChanged = false,
23388         dir, day, month,
23389         newDate, newViewDate;
23390         
23391         switch(e.keyCode){
23392             case 27: // escape
23393                 this.hidePopup();
23394                 e.preventDefault();
23395                 break;
23396             case 37: // left
23397             case 39: // right
23398                 if (!this.keyboardNavigation) {
23399                     break;
23400                 }
23401                 dir = e.keyCode == 37 ? -1 : 1;
23402                 
23403                 if (e.ctrlKey){
23404                     newDate = this.moveYear(this.date, dir);
23405                     newViewDate = this.moveYear(this.viewDate, dir);
23406                 } else if (e.shiftKey){
23407                     newDate = this.moveMonth(this.date, dir);
23408                     newViewDate = this.moveMonth(this.viewDate, dir);
23409                 } else {
23410                     newDate = new Date(this.date);
23411                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23412                     newViewDate = new Date(this.viewDate);
23413                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23414                 }
23415                 if (this.dateWithinRange(newDate)){
23416                     this.date = newDate;
23417                     this.viewDate = newViewDate;
23418                     this.setValue(this.formatDate(this.date));
23419 //                    this.update();
23420                     e.preventDefault();
23421                     dateChanged = true;
23422                 }
23423                 break;
23424             case 38: // up
23425             case 40: // down
23426                 if (!this.keyboardNavigation) {
23427                     break;
23428                 }
23429                 dir = e.keyCode == 38 ? -1 : 1;
23430                 if (e.ctrlKey){
23431                     newDate = this.moveYear(this.date, dir);
23432                     newViewDate = this.moveYear(this.viewDate, dir);
23433                 } else if (e.shiftKey){
23434                     newDate = this.moveMonth(this.date, dir);
23435                     newViewDate = this.moveMonth(this.viewDate, dir);
23436                 } else {
23437                     newDate = new Date(this.date);
23438                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23439                     newViewDate = new Date(this.viewDate);
23440                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23441                 }
23442                 if (this.dateWithinRange(newDate)){
23443                     this.date = newDate;
23444                     this.viewDate = newViewDate;
23445                     this.setValue(this.formatDate(this.date));
23446 //                    this.update();
23447                     e.preventDefault();
23448                     dateChanged = true;
23449                 }
23450                 break;
23451             case 13: // enter
23452                 this.setValue(this.formatDate(this.date));
23453                 this.hidePopup();
23454                 e.preventDefault();
23455                 break;
23456             case 9: // tab
23457                 this.setValue(this.formatDate(this.date));
23458                 this.hidePopup();
23459                 break;
23460             case 16: // shift
23461             case 17: // ctrl
23462             case 18: // alt
23463                 break;
23464             default :
23465                 this.hidePopup();
23466                 
23467         }
23468     },
23469     
23470     
23471     onClick: function(e) 
23472     {
23473         e.stopPropagation();
23474         e.preventDefault();
23475         
23476         var target = e.getTarget();
23477         
23478         if(target.nodeName.toLowerCase() === 'i'){
23479             target = Roo.get(target).dom.parentNode;
23480         }
23481         
23482         var nodeName = target.nodeName;
23483         var className = target.className;
23484         var html = target.innerHTML;
23485         //Roo.log(nodeName);
23486         
23487         switch(nodeName.toLowerCase()) {
23488             case 'th':
23489                 switch(className) {
23490                     case 'switch':
23491                         this.showMode(1);
23492                         break;
23493                     case 'prev':
23494                     case 'next':
23495                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23496                         switch(this.viewMode){
23497                                 case 0:
23498                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23499                                         break;
23500                                 case 1:
23501                                 case 2:
23502                                         this.viewDate = this.moveYear(this.viewDate, dir);
23503                                         break;
23504                         }
23505                         this.fill();
23506                         break;
23507                     case 'today':
23508                         var date = new Date();
23509                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23510 //                        this.fill()
23511                         this.setValue(this.formatDate(this.date));
23512                         
23513                         this.hidePopup();
23514                         break;
23515                 }
23516                 break;
23517             case 'span':
23518                 if (className.indexOf('disabled') < 0) {
23519                 if (!this.viewDate) {
23520                     this.viewDate = new Date();
23521                 }
23522                 this.viewDate.setUTCDate(1);
23523                     if (className.indexOf('month') > -1) {
23524                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23525                     } else {
23526                         var year = parseInt(html, 10) || 0;
23527                         this.viewDate.setUTCFullYear(year);
23528                         
23529                     }
23530                     
23531                     if(this.singleMode){
23532                         this.setValue(this.formatDate(this.viewDate));
23533                         this.hidePopup();
23534                         return;
23535                     }
23536                     
23537                     this.showMode(-1);
23538                     this.fill();
23539                 }
23540                 break;
23541                 
23542             case 'td':
23543                 //Roo.log(className);
23544                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23545                     var day = parseInt(html, 10) || 1;
23546                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23547                         month = (this.viewDate || new Date()).getUTCMonth();
23548
23549                     if (className.indexOf('old') > -1) {
23550                         if(month === 0 ){
23551                             month = 11;
23552                             year -= 1;
23553                         }else{
23554                             month -= 1;
23555                         }
23556                     } else if (className.indexOf('new') > -1) {
23557                         if (month == 11) {
23558                             month = 0;
23559                             year += 1;
23560                         } else {
23561                             month += 1;
23562                         }
23563                     }
23564                     //Roo.log([year,month,day]);
23565                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23566                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23567 //                    this.fill();
23568                     //Roo.log(this.formatDate(this.date));
23569                     this.setValue(this.formatDate(this.date));
23570                     this.hidePopup();
23571                 }
23572                 break;
23573         }
23574     },
23575     
23576     setStartDate: function(startDate)
23577     {
23578         this.startDate = startDate || -Infinity;
23579         if (this.startDate !== -Infinity) {
23580             this.startDate = this.parseDate(this.startDate);
23581         }
23582         this.update();
23583         this.updateNavArrows();
23584     },
23585
23586     setEndDate: function(endDate)
23587     {
23588         this.endDate = endDate || Infinity;
23589         if (this.endDate !== Infinity) {
23590             this.endDate = this.parseDate(this.endDate);
23591         }
23592         this.update();
23593         this.updateNavArrows();
23594     },
23595     
23596     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23597     {
23598         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23599         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23600             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23601         }
23602         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23603             return parseInt(d, 10);
23604         });
23605         this.update();
23606         this.updateNavArrows();
23607     },
23608     
23609     updateNavArrows: function() 
23610     {
23611         if(this.singleMode){
23612             return;
23613         }
23614         
23615         var d = new Date(this.viewDate),
23616         year = d.getUTCFullYear(),
23617         month = d.getUTCMonth();
23618         
23619         Roo.each(this.picker().select('.prev', true).elements, function(v){
23620             v.show();
23621             switch (this.viewMode) {
23622                 case 0:
23623
23624                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23625                         v.hide();
23626                     }
23627                     break;
23628                 case 1:
23629                 case 2:
23630                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23631                         v.hide();
23632                     }
23633                     break;
23634             }
23635         });
23636         
23637         Roo.each(this.picker().select('.next', true).elements, function(v){
23638             v.show();
23639             switch (this.viewMode) {
23640                 case 0:
23641
23642                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23643                         v.hide();
23644                     }
23645                     break;
23646                 case 1:
23647                 case 2:
23648                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23649                         v.hide();
23650                     }
23651                     break;
23652             }
23653         })
23654     },
23655     
23656     moveMonth: function(date, dir)
23657     {
23658         if (!dir) {
23659             return date;
23660         }
23661         var new_date = new Date(date.valueOf()),
23662         day = new_date.getUTCDate(),
23663         month = new_date.getUTCMonth(),
23664         mag = Math.abs(dir),
23665         new_month, test;
23666         dir = dir > 0 ? 1 : -1;
23667         if (mag == 1){
23668             test = dir == -1
23669             // If going back one month, make sure month is not current month
23670             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23671             ? function(){
23672                 return new_date.getUTCMonth() == month;
23673             }
23674             // If going forward one month, make sure month is as expected
23675             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23676             : function(){
23677                 return new_date.getUTCMonth() != new_month;
23678             };
23679             new_month = month + dir;
23680             new_date.setUTCMonth(new_month);
23681             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23682             if (new_month < 0 || new_month > 11) {
23683                 new_month = (new_month + 12) % 12;
23684             }
23685         } else {
23686             // For magnitudes >1, move one month at a time...
23687             for (var i=0; i<mag; i++) {
23688                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23689                 new_date = this.moveMonth(new_date, dir);
23690             }
23691             // ...then reset the day, keeping it in the new month
23692             new_month = new_date.getUTCMonth();
23693             new_date.setUTCDate(day);
23694             test = function(){
23695                 return new_month != new_date.getUTCMonth();
23696             };
23697         }
23698         // Common date-resetting loop -- if date is beyond end of month, make it
23699         // end of month
23700         while (test()){
23701             new_date.setUTCDate(--day);
23702             new_date.setUTCMonth(new_month);
23703         }
23704         return new_date;
23705     },
23706
23707     moveYear: function(date, dir)
23708     {
23709         return this.moveMonth(date, dir*12);
23710     },
23711
23712     dateWithinRange: function(date)
23713     {
23714         return date >= this.startDate && date <= this.endDate;
23715     },
23716
23717     
23718     remove: function() 
23719     {
23720         this.picker().remove();
23721     },
23722     
23723     validateValue : function(value)
23724     {
23725         if(this.getVisibilityEl().hasClass('hidden')){
23726             return true;
23727         }
23728         
23729         if(value.length < 1)  {
23730             if(this.allowBlank){
23731                 return true;
23732             }
23733             return false;
23734         }
23735         
23736         if(value.length < this.minLength){
23737             return false;
23738         }
23739         if(value.length > this.maxLength){
23740             return false;
23741         }
23742         if(this.vtype){
23743             var vt = Roo.form.VTypes;
23744             if(!vt[this.vtype](value, this)){
23745                 return false;
23746             }
23747         }
23748         if(typeof this.validator == "function"){
23749             var msg = this.validator(value);
23750             if(msg !== true){
23751                 return false;
23752             }
23753         }
23754         
23755         if(this.regex && !this.regex.test(value)){
23756             return false;
23757         }
23758         
23759         if(typeof(this.parseDate(value)) == 'undefined'){
23760             return false;
23761         }
23762         
23763         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23764             return false;
23765         }      
23766         
23767         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23768             return false;
23769         } 
23770         
23771         
23772         return true;
23773     },
23774     
23775     reset : function()
23776     {
23777         this.date = this.viewDate = '';
23778         
23779         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23780     }
23781    
23782 });
23783
23784 Roo.apply(Roo.bootstrap.form.DateField,  {
23785     
23786     head : {
23787         tag: 'thead',
23788         cn: [
23789         {
23790             tag: 'tr',
23791             cn: [
23792             {
23793                 tag: 'th',
23794                 cls: 'prev',
23795                 html: '<i class="fa fa-arrow-left"/>'
23796             },
23797             {
23798                 tag: 'th',
23799                 cls: 'switch',
23800                 colspan: '5'
23801             },
23802             {
23803                 tag: 'th',
23804                 cls: 'next',
23805                 html: '<i class="fa fa-arrow-right"/>'
23806             }
23807
23808             ]
23809         }
23810         ]
23811     },
23812     
23813     content : {
23814         tag: 'tbody',
23815         cn: [
23816         {
23817             tag: 'tr',
23818             cn: [
23819             {
23820                 tag: 'td',
23821                 colspan: '7'
23822             }
23823             ]
23824         }
23825         ]
23826     },
23827     
23828     footer : {
23829         tag: 'tfoot',
23830         cn: [
23831         {
23832             tag: 'tr',
23833             cn: [
23834             {
23835                 tag: 'th',
23836                 colspan: '7',
23837                 cls: 'today'
23838             }
23839                     
23840             ]
23841         }
23842         ]
23843     },
23844     
23845     dates:{
23846         en: {
23847             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23848             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23849             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23850             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23851             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23852             today: "Today"
23853         }
23854     },
23855     
23856     modes: [
23857     {
23858         clsName: 'days',
23859         navFnc: 'Month',
23860         navStep: 1
23861     },
23862     {
23863         clsName: 'months',
23864         navFnc: 'FullYear',
23865         navStep: 1
23866     },
23867     {
23868         clsName: 'years',
23869         navFnc: 'FullYear',
23870         navStep: 10
23871     }]
23872 });
23873
23874 Roo.apply(Roo.bootstrap.form.DateField,  {
23875   
23876     template : {
23877         tag: 'div',
23878         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23879         cn: [
23880         {
23881             tag: 'div',
23882             cls: 'datepicker-days',
23883             cn: [
23884             {
23885                 tag: 'table',
23886                 cls: 'table-condensed',
23887                 cn:[
23888                 Roo.bootstrap.form.DateField.head,
23889                 {
23890                     tag: 'tbody'
23891                 },
23892                 Roo.bootstrap.form.DateField.footer
23893                 ]
23894             }
23895             ]
23896         },
23897         {
23898             tag: 'div',
23899             cls: 'datepicker-months',
23900             cn: [
23901             {
23902                 tag: 'table',
23903                 cls: 'table-condensed',
23904                 cn:[
23905                 Roo.bootstrap.form.DateField.head,
23906                 Roo.bootstrap.form.DateField.content,
23907                 Roo.bootstrap.form.DateField.footer
23908                 ]
23909             }
23910             ]
23911         },
23912         {
23913             tag: 'div',
23914             cls: 'datepicker-years',
23915             cn: [
23916             {
23917                 tag: 'table',
23918                 cls: 'table-condensed',
23919                 cn:[
23920                 Roo.bootstrap.form.DateField.head,
23921                 Roo.bootstrap.form.DateField.content,
23922                 Roo.bootstrap.form.DateField.footer
23923                 ]
23924             }
23925             ]
23926         }
23927         ]
23928     }
23929 });
23930
23931  
23932
23933  /*
23934  * - LGPL
23935  *
23936  * TimeField
23937  * 
23938  */
23939
23940 /**
23941  * @class Roo.bootstrap.form.TimeField
23942  * @extends Roo.bootstrap.form.Input
23943  * Bootstrap DateField class
23944  * 
23945  * 
23946  * @constructor
23947  * Create a new TimeField
23948  * @param {Object} config The config object
23949  */
23950
23951 Roo.bootstrap.form.TimeField = function(config){
23952     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23953     this.addEvents({
23954             /**
23955              * @event show
23956              * Fires when this field show.
23957              * @param {Roo.bootstrap.form.DateField} thisthis
23958              * @param {Mixed} date The date value
23959              */
23960             show : true,
23961             /**
23962              * @event show
23963              * Fires when this field hide.
23964              * @param {Roo.bootstrap.form.DateField} this
23965              * @param {Mixed} date The date value
23966              */
23967             hide : true,
23968             /**
23969              * @event select
23970              * Fires when select a date.
23971              * @param {Roo.bootstrap.form.DateField} this
23972              * @param {Mixed} date The date value
23973              */
23974             select : true
23975         });
23976 };
23977
23978 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23979     
23980     /**
23981      * @cfg {String} format
23982      * The default time format string which can be overriden for localization support.  The format must be
23983      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23984      */
23985     format : "H:i",
23986
23987     getAutoCreate : function()
23988     {
23989         this.after = '<i class="fa far fa-clock"></i>';
23990         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23991         
23992          
23993     },
23994     onRender: function(ct, position)
23995     {
23996         
23997         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23998                 
23999         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24000         
24001         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24002         
24003         this.pop = this.picker().select('>.datepicker-time',true).first();
24004         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24005         
24006         this.picker().on('mousedown', this.onMousedown, this);
24007         this.picker().on('click', this.onClick, this);
24008         
24009         this.picker().addClass('datepicker-dropdown');
24010     
24011         this.fillTime();
24012         this.update();
24013             
24014         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24015         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24016         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24017         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24018         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24019         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24020
24021     },
24022     
24023     fireKey: function(e){
24024         if (!this.picker().isVisible()){
24025             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24026                 this.show();
24027             }
24028             return;
24029         }
24030
24031         e.preventDefault();
24032         
24033         switch(e.keyCode){
24034             case 27: // escape
24035                 this.hide();
24036                 break;
24037             case 37: // left
24038             case 39: // right
24039                 this.onTogglePeriod();
24040                 break;
24041             case 38: // up
24042                 this.onIncrementMinutes();
24043                 break;
24044             case 40: // down
24045                 this.onDecrementMinutes();
24046                 break;
24047             case 13: // enter
24048             case 9: // tab
24049                 this.setTime();
24050                 break;
24051         }
24052     },
24053     
24054     onClick: function(e) {
24055         e.stopPropagation();
24056         e.preventDefault();
24057     },
24058     
24059     picker : function()
24060     {
24061         return this.pickerEl;
24062     },
24063     
24064     fillTime: function()
24065     {    
24066         var time = this.pop.select('tbody', true).first();
24067         
24068         time.dom.innerHTML = '';
24069         
24070         time.createChild({
24071             tag: 'tr',
24072             cn: [
24073                 {
24074                     tag: 'td',
24075                     cn: [
24076                         {
24077                             tag: 'a',
24078                             href: '#',
24079                             cls: 'btn',
24080                             cn: [
24081                                 {
24082                                     tag: 'i',
24083                                     cls: 'hours-up fa fas fa-chevron-up'
24084                                 }
24085                             ]
24086                         } 
24087                     ]
24088                 },
24089                 {
24090                     tag: 'td',
24091                     cls: 'separator'
24092                 },
24093                 {
24094                     tag: 'td',
24095                     cn: [
24096                         {
24097                             tag: 'a',
24098                             href: '#',
24099                             cls: 'btn',
24100                             cn: [
24101                                 {
24102                                     tag: 'i',
24103                                     cls: 'minutes-up fa fas fa-chevron-up'
24104                                 }
24105                             ]
24106                         }
24107                     ]
24108                 },
24109                 {
24110                     tag: 'td',
24111                     cls: 'separator'
24112                 }
24113             ]
24114         });
24115         
24116         time.createChild({
24117             tag: 'tr',
24118             cn: [
24119                 {
24120                     tag: 'td',
24121                     cn: [
24122                         {
24123                             tag: 'span',
24124                             cls: 'timepicker-hour',
24125                             html: '00'
24126                         }  
24127                     ]
24128                 },
24129                 {
24130                     tag: 'td',
24131                     cls: 'separator',
24132                     html: ':'
24133                 },
24134                 {
24135                     tag: 'td',
24136                     cn: [
24137                         {
24138                             tag: 'span',
24139                             cls: 'timepicker-minute',
24140                             html: '00'
24141                         }  
24142                     ]
24143                 },
24144                 {
24145                     tag: 'td',
24146                     cls: 'separator'
24147                 },
24148                 {
24149                     tag: 'td',
24150                     cn: [
24151                         {
24152                             tag: 'button',
24153                             type: 'button',
24154                             cls: 'btn btn-primary period',
24155                             html: 'AM'
24156                             
24157                         }
24158                     ]
24159                 }
24160             ]
24161         });
24162         
24163         time.createChild({
24164             tag: 'tr',
24165             cn: [
24166                 {
24167                     tag: 'td',
24168                     cn: [
24169                         {
24170                             tag: 'a',
24171                             href: '#',
24172                             cls: 'btn',
24173                             cn: [
24174                                 {
24175                                     tag: 'span',
24176                                     cls: 'hours-down fa fas fa-chevron-down'
24177                                 }
24178                             ]
24179                         }
24180                     ]
24181                 },
24182                 {
24183                     tag: 'td',
24184                     cls: 'separator'
24185                 },
24186                 {
24187                     tag: 'td',
24188                     cn: [
24189                         {
24190                             tag: 'a',
24191                             href: '#',
24192                             cls: 'btn',
24193                             cn: [
24194                                 {
24195                                     tag: 'span',
24196                                     cls: 'minutes-down fa fas fa-chevron-down'
24197                                 }
24198                             ]
24199                         }
24200                     ]
24201                 },
24202                 {
24203                     tag: 'td',
24204                     cls: 'separator'
24205                 }
24206             ]
24207         });
24208         
24209     },
24210     
24211     update: function()
24212     {
24213         
24214         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24215         
24216         this.fill();
24217     },
24218     
24219     fill: function() 
24220     {
24221         var hours = this.time.getHours();
24222         var minutes = this.time.getMinutes();
24223         var period = 'AM';
24224         
24225         if(hours > 11){
24226             period = 'PM';
24227         }
24228         
24229         if(hours == 0){
24230             hours = 12;
24231         }
24232         
24233         
24234         if(hours > 12){
24235             hours = hours - 12;
24236         }
24237         
24238         if(hours < 10){
24239             hours = '0' + hours;
24240         }
24241         
24242         if(minutes < 10){
24243             minutes = '0' + minutes;
24244         }
24245         
24246         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24247         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24248         this.pop.select('button', true).first().dom.innerHTML = period;
24249         
24250     },
24251     
24252     place: function()
24253     {   
24254         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24255         
24256         var cls = ['bottom'];
24257         
24258         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24259             cls.pop();
24260             cls.push('top');
24261         }
24262         
24263         cls.push('right');
24264         
24265         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24266             cls.pop();
24267             cls.push('left');
24268         }
24269         //this.picker().setXY(20000,20000);
24270         this.picker().addClass(cls.join('-'));
24271         
24272         var _this = this;
24273         
24274         Roo.each(cls, function(c){
24275             if(c == 'bottom'){
24276                 (function() {
24277                  //  
24278                 }).defer(200);
24279                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24280                 //_this.picker().setTop(_this.inputEl().getHeight());
24281                 return;
24282             }
24283             if(c == 'top'){
24284                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24285                 
24286                 //_this.picker().setTop(0 - _this.picker().getHeight());
24287                 return;
24288             }
24289             /*
24290             if(c == 'left'){
24291                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24292                 return;
24293             }
24294             if(c == 'right'){
24295                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24296                 return;
24297             }
24298             */
24299         });
24300         
24301     },
24302   
24303     onFocus : function()
24304     {
24305         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24306         this.show();
24307     },
24308     
24309     onBlur : function()
24310     {
24311         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24312         this.hide();
24313     },
24314     
24315     show : function()
24316     {
24317         this.picker().show();
24318         this.pop.show();
24319         this.update();
24320         this.place();
24321         
24322         this.fireEvent('show', this, this.date);
24323     },
24324     
24325     hide : function()
24326     {
24327         this.picker().hide();
24328         this.pop.hide();
24329         
24330         this.fireEvent('hide', this, this.date);
24331     },
24332     
24333     setTime : function()
24334     {
24335         this.hide();
24336         this.setValue(this.time.format(this.format));
24337         
24338         this.fireEvent('select', this, this.date);
24339         
24340         
24341     },
24342     
24343     onMousedown: function(e){
24344         e.stopPropagation();
24345         e.preventDefault();
24346     },
24347     
24348     onIncrementHours: function()
24349     {
24350         Roo.log('onIncrementHours');
24351         this.time = this.time.add(Date.HOUR, 1);
24352         this.update();
24353         
24354     },
24355     
24356     onDecrementHours: function()
24357     {
24358         Roo.log('onDecrementHours');
24359         this.time = this.time.add(Date.HOUR, -1);
24360         this.update();
24361     },
24362     
24363     onIncrementMinutes: function()
24364     {
24365         Roo.log('onIncrementMinutes');
24366         this.time = this.time.add(Date.MINUTE, 1);
24367         this.update();
24368     },
24369     
24370     onDecrementMinutes: function()
24371     {
24372         Roo.log('onDecrementMinutes');
24373         this.time = this.time.add(Date.MINUTE, -1);
24374         this.update();
24375     },
24376     
24377     onTogglePeriod: function()
24378     {
24379         Roo.log('onTogglePeriod');
24380         this.time = this.time.add(Date.HOUR, 12);
24381         this.update();
24382     }
24383     
24384    
24385 });
24386  
24387
24388 Roo.apply(Roo.bootstrap.form.TimeField,  {
24389   
24390     template : {
24391         tag: 'div',
24392         cls: 'datepicker dropdown-menu',
24393         cn: [
24394             {
24395                 tag: 'div',
24396                 cls: 'datepicker-time',
24397                 cn: [
24398                 {
24399                     tag: 'table',
24400                     cls: 'table-condensed',
24401                     cn:[
24402                         {
24403                             tag: 'tbody',
24404                             cn: [
24405                                 {
24406                                     tag: 'tr',
24407                                     cn: [
24408                                     {
24409                                         tag: 'td',
24410                                         colspan: '7'
24411                                     }
24412                                     ]
24413                                 }
24414                             ]
24415                         },
24416                         {
24417                             tag: 'tfoot',
24418                             cn: [
24419                                 {
24420                                     tag: 'tr',
24421                                     cn: [
24422                                     {
24423                                         tag: 'th',
24424                                         colspan: '7',
24425                                         cls: '',
24426                                         cn: [
24427                                             {
24428                                                 tag: 'button',
24429                                                 cls: 'btn btn-info ok',
24430                                                 html: 'OK'
24431                                             }
24432                                         ]
24433                                     }
24434                     
24435                                     ]
24436                                 }
24437                             ]
24438                         }
24439                     ]
24440                 }
24441                 ]
24442             }
24443         ]
24444     }
24445 });
24446
24447  
24448
24449  /*
24450  * - LGPL
24451  *
24452  * MonthField
24453  * 
24454  */
24455
24456 /**
24457  * @class Roo.bootstrap.form.MonthField
24458  * @extends Roo.bootstrap.form.Input
24459  * Bootstrap MonthField class
24460  * 
24461  * @cfg {String} language default en
24462  * 
24463  * @constructor
24464  * Create a new MonthField
24465  * @param {Object} config The config object
24466  */
24467
24468 Roo.bootstrap.form.MonthField = function(config){
24469     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24470     
24471     this.addEvents({
24472         /**
24473          * @event show
24474          * Fires when this field show.
24475          * @param {Roo.bootstrap.form.MonthField} this
24476          * @param {Mixed} date The date value
24477          */
24478         show : true,
24479         /**
24480          * @event show
24481          * Fires when this field hide.
24482          * @param {Roo.bootstrap.form.MonthField} this
24483          * @param {Mixed} date The date value
24484          */
24485         hide : true,
24486         /**
24487          * @event select
24488          * Fires when select a date.
24489          * @param {Roo.bootstrap.form.MonthField} this
24490          * @param {String} oldvalue The old value
24491          * @param {String} newvalue The new value
24492          */
24493         select : true
24494     });
24495 };
24496
24497 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24498     
24499     onRender: function(ct, position)
24500     {
24501         
24502         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24503         
24504         this.language = this.language || 'en';
24505         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24506         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24507         
24508         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24509         this.isInline = false;
24510         this.isInput = true;
24511         this.component = this.el.select('.add-on', true).first() || false;
24512         this.component = (this.component && this.component.length === 0) ? false : this.component;
24513         this.hasInput = this.component && this.inputEL().length;
24514         
24515         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24516         
24517         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24518         
24519         this.picker().on('mousedown', this.onMousedown, this);
24520         this.picker().on('click', this.onClick, this);
24521         
24522         this.picker().addClass('datepicker-dropdown');
24523         
24524         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24525             v.setStyle('width', '189px');
24526         });
24527         
24528         this.fillMonths();
24529         
24530         this.update();
24531         
24532         if(this.isInline) {
24533             this.show();
24534         }
24535         
24536     },
24537     
24538     setValue: function(v, suppressEvent)
24539     {   
24540         var o = this.getValue();
24541         
24542         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24543         
24544         this.update();
24545
24546         if(suppressEvent !== true){
24547             this.fireEvent('select', this, o, v);
24548         }
24549         
24550     },
24551     
24552     getValue: function()
24553     {
24554         return this.value;
24555     },
24556     
24557     onClick: function(e) 
24558     {
24559         e.stopPropagation();
24560         e.preventDefault();
24561         
24562         var target = e.getTarget();
24563         
24564         if(target.nodeName.toLowerCase() === 'i'){
24565             target = Roo.get(target).dom.parentNode;
24566         }
24567         
24568         var nodeName = target.nodeName;
24569         var className = target.className;
24570         var html = target.innerHTML;
24571         
24572         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24573             return;
24574         }
24575         
24576         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24577         
24578         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24579         
24580         this.hide();
24581                         
24582     },
24583     
24584     picker : function()
24585     {
24586         return this.pickerEl;
24587     },
24588     
24589     fillMonths: function()
24590     {    
24591         var i = 0;
24592         var months = this.picker().select('>.datepicker-months td', true).first();
24593         
24594         months.dom.innerHTML = '';
24595         
24596         while (i < 12) {
24597             var month = {
24598                 tag: 'span',
24599                 cls: 'month',
24600                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24601             };
24602             
24603             months.createChild(month);
24604         }
24605         
24606     },
24607     
24608     update: function()
24609     {
24610         var _this = this;
24611         
24612         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24613             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24614         }
24615         
24616         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24617             e.removeClass('active');
24618             
24619             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24620                 e.addClass('active');
24621             }
24622         })
24623     },
24624     
24625     place: function()
24626     {
24627         if(this.isInline) {
24628             return;
24629         }
24630         
24631         this.picker().removeClass(['bottom', 'top']);
24632         
24633         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24634             /*
24635              * place to the top of element!
24636              *
24637              */
24638             
24639             this.picker().addClass('top');
24640             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24641             
24642             return;
24643         }
24644         
24645         this.picker().addClass('bottom');
24646         
24647         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24648     },
24649     
24650     onFocus : function()
24651     {
24652         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24653         this.show();
24654     },
24655     
24656     onBlur : function()
24657     {
24658         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24659         
24660         var d = this.inputEl().getValue();
24661         
24662         this.setValue(d);
24663                 
24664         this.hide();
24665     },
24666     
24667     show : function()
24668     {
24669         this.picker().show();
24670         this.picker().select('>.datepicker-months', true).first().show();
24671         this.update();
24672         this.place();
24673         
24674         this.fireEvent('show', this, this.date);
24675     },
24676     
24677     hide : function()
24678     {
24679         if(this.isInline) {
24680             return;
24681         }
24682         this.picker().hide();
24683         this.fireEvent('hide', this, this.date);
24684         
24685     },
24686     
24687     onMousedown: function(e)
24688     {
24689         e.stopPropagation();
24690         e.preventDefault();
24691     },
24692     
24693     keyup: function(e)
24694     {
24695         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24696         this.update();
24697     },
24698
24699     fireKey: function(e)
24700     {
24701         if (!this.picker().isVisible()){
24702             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24703                 this.show();
24704             }
24705             return;
24706         }
24707         
24708         var dir;
24709         
24710         switch(e.keyCode){
24711             case 27: // escape
24712                 this.hide();
24713                 e.preventDefault();
24714                 break;
24715             case 37: // left
24716             case 39: // right
24717                 dir = e.keyCode == 37 ? -1 : 1;
24718                 
24719                 this.vIndex = this.vIndex + dir;
24720                 
24721                 if(this.vIndex < 0){
24722                     this.vIndex = 0;
24723                 }
24724                 
24725                 if(this.vIndex > 11){
24726                     this.vIndex = 11;
24727                 }
24728                 
24729                 if(isNaN(this.vIndex)){
24730                     this.vIndex = 0;
24731                 }
24732                 
24733                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24734                 
24735                 break;
24736             case 38: // up
24737             case 40: // down
24738                 
24739                 dir = e.keyCode == 38 ? -1 : 1;
24740                 
24741                 this.vIndex = this.vIndex + dir * 4;
24742                 
24743                 if(this.vIndex < 0){
24744                     this.vIndex = 0;
24745                 }
24746                 
24747                 if(this.vIndex > 11){
24748                     this.vIndex = 11;
24749                 }
24750                 
24751                 if(isNaN(this.vIndex)){
24752                     this.vIndex = 0;
24753                 }
24754                 
24755                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24756                 break;
24757                 
24758             case 13: // enter
24759                 
24760                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24761                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24762                 }
24763                 
24764                 this.hide();
24765                 e.preventDefault();
24766                 break;
24767             case 9: // tab
24768                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24769                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24770                 }
24771                 this.hide();
24772                 break;
24773             case 16: // shift
24774             case 17: // ctrl
24775             case 18: // alt
24776                 break;
24777             default :
24778                 this.hide();
24779                 
24780         }
24781     },
24782     
24783     remove: function() 
24784     {
24785         this.picker().remove();
24786     }
24787    
24788 });
24789
24790 Roo.apply(Roo.bootstrap.form.MonthField,  {
24791     
24792     content : {
24793         tag: 'tbody',
24794         cn: [
24795         {
24796             tag: 'tr',
24797             cn: [
24798             {
24799                 tag: 'td',
24800                 colspan: '7'
24801             }
24802             ]
24803         }
24804         ]
24805     },
24806     
24807     dates:{
24808         en: {
24809             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24810             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24811         }
24812     }
24813 });
24814
24815 Roo.apply(Roo.bootstrap.form.MonthField,  {
24816   
24817     template : {
24818         tag: 'div',
24819         cls: 'datepicker dropdown-menu roo-dynamic',
24820         cn: [
24821             {
24822                 tag: 'div',
24823                 cls: 'datepicker-months',
24824                 cn: [
24825                 {
24826                     tag: 'table',
24827                     cls: 'table-condensed',
24828                     cn:[
24829                         Roo.bootstrap.form.DateField.content
24830                     ]
24831                 }
24832                 ]
24833             }
24834         ]
24835     }
24836 });
24837
24838  
24839
24840  
24841  /*
24842  * - LGPL
24843  *
24844  * CheckBox
24845  * 
24846  */
24847
24848 /**
24849  * @class Roo.bootstrap.form.CheckBox
24850  * @extends Roo.bootstrap.form.Input
24851  * Bootstrap CheckBox class
24852  * 
24853  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24854  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24855  * @cfg {String} boxLabel The text that appears beside the checkbox
24856  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24857  * @cfg {Boolean} checked initnal the element
24858  * @cfg {Boolean} inline inline the element (default false)
24859  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24860  * @cfg {String} tooltip label tooltip
24861  * 
24862  * @constructor
24863  * Create a new CheckBox
24864  * @param {Object} config The config object
24865  */
24866
24867 Roo.bootstrap.form.CheckBox = function(config){
24868     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24869    
24870     this.addEvents({
24871         /**
24872         * @event check
24873         * Fires when the element is checked or unchecked.
24874         * @param {Roo.bootstrap.form.CheckBox} this This input
24875         * @param {Boolean} checked The new checked value
24876         */
24877        check : true,
24878        /**
24879         * @event click
24880         * Fires when the element is click.
24881         * @param {Roo.bootstrap.form.CheckBox} this This input
24882         */
24883        click : true
24884     });
24885     
24886 };
24887
24888 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24889   
24890     inputType: 'checkbox',
24891     inputValue: 1,
24892     valueOff: 0,
24893     boxLabel: false,
24894     checked: false,
24895     weight : false,
24896     inline: false,
24897     tooltip : '',
24898     
24899     // checkbox success does not make any sense really.. 
24900     invalidClass : "",
24901     validClass : "",
24902     
24903     
24904     getAutoCreate : function()
24905     {
24906         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24907         
24908         var id = Roo.id();
24909         
24910         var cfg = {};
24911         
24912         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24913         
24914         if(this.inline){
24915             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24916         }
24917         
24918         var input =  {
24919             tag: 'input',
24920             id : id,
24921             type : this.inputType,
24922             value : this.inputValue,
24923             cls : 'roo-' + this.inputType, //'form-box',
24924             placeholder : this.placeholder || ''
24925             
24926         };
24927         
24928         if(this.inputType != 'radio'){
24929             var hidden =  {
24930                 tag: 'input',
24931                 type : 'hidden',
24932                 cls : 'roo-hidden-value',
24933                 value : this.checked ? this.inputValue : this.valueOff
24934             };
24935         }
24936         
24937             
24938         if (this.weight) { // Validity check?
24939             cfg.cls += " " + this.inputType + "-" + this.weight;
24940         }
24941         
24942         if (this.disabled) {
24943             input.disabled=true;
24944         }
24945         
24946         if(this.checked){
24947             input.checked = this.checked;
24948         }
24949         
24950         if (this.name) {
24951             
24952             input.name = this.name;
24953             
24954             if(this.inputType != 'radio'){
24955                 hidden.name = this.name;
24956                 input.name = '_hidden_' + this.name;
24957             }
24958         }
24959         
24960         if (this.size) {
24961             input.cls += ' input-' + this.size;
24962         }
24963         
24964         var settings=this;
24965         
24966         ['xs','sm','md','lg'].map(function(size){
24967             if (settings[size]) {
24968                 cfg.cls += ' col-' + size + '-' + settings[size];
24969             }
24970         });
24971         
24972         var inputblock = input;
24973          
24974         if (this.before || this.after) {
24975             
24976             inputblock = {
24977                 cls : 'input-group',
24978                 cn :  [] 
24979             };
24980             
24981             if (this.before) {
24982                 inputblock.cn.push({
24983                     tag :'span',
24984                     cls : 'input-group-addon',
24985                     html : this.before
24986                 });
24987             }
24988             
24989             inputblock.cn.push(input);
24990             
24991             if(this.inputType != 'radio'){
24992                 inputblock.cn.push(hidden);
24993             }
24994             
24995             if (this.after) {
24996                 inputblock.cn.push({
24997                     tag :'span',
24998                     cls : 'input-group-addon',
24999                     html : this.after
25000                 });
25001             }
25002             
25003         }
25004         var boxLabelCfg = false;
25005         
25006         if(this.boxLabel){
25007            
25008             boxLabelCfg = {
25009                 tag: 'label',
25010                 //'for': id, // box label is handled by onclick - so no for...
25011                 cls: 'box-label',
25012                 html: this.boxLabel
25013             };
25014             if(this.tooltip){
25015                 boxLabelCfg.tooltip = this.tooltip;
25016             }
25017              
25018         }
25019         
25020         
25021         if (align ==='left' && this.fieldLabel.length) {
25022 //                Roo.log("left and has label");
25023             cfg.cn = [
25024                 {
25025                     tag: 'label',
25026                     'for' :  id,
25027                     cls : 'control-label',
25028                     html : this.fieldLabel
25029                 },
25030                 {
25031                     cls : "", 
25032                     cn: [
25033                         inputblock
25034                     ]
25035                 }
25036             ];
25037             
25038             if (boxLabelCfg) {
25039                 cfg.cn[1].cn.push(boxLabelCfg);
25040             }
25041             
25042             if(this.labelWidth > 12){
25043                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25044             }
25045             
25046             if(this.labelWidth < 13 && this.labelmd == 0){
25047                 this.labelmd = this.labelWidth;
25048             }
25049             
25050             if(this.labellg > 0){
25051                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25052                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25053             }
25054             
25055             if(this.labelmd > 0){
25056                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25057                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25058             }
25059             
25060             if(this.labelsm > 0){
25061                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25062                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25063             }
25064             
25065             if(this.labelxs > 0){
25066                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25067                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25068             }
25069             
25070         } else if ( this.fieldLabel.length) {
25071 //                Roo.log(" label");
25072                 cfg.cn = [
25073                    
25074                     {
25075                         tag: this.boxLabel ? 'span' : 'label',
25076                         'for': id,
25077                         cls: 'control-label box-input-label',
25078                         //cls : 'input-group-addon',
25079                         html : this.fieldLabel
25080                     },
25081                     
25082                     inputblock
25083                     
25084                 ];
25085                 if (boxLabelCfg) {
25086                     cfg.cn.push(boxLabelCfg);
25087                 }
25088
25089         } else {
25090             
25091 //                Roo.log(" no label && no align");
25092                 cfg.cn = [  inputblock ] ;
25093                 if (boxLabelCfg) {
25094                     cfg.cn.push(boxLabelCfg);
25095                 }
25096
25097                 
25098         }
25099         
25100        
25101         
25102         if(this.inputType != 'radio'){
25103             cfg.cn.push(hidden);
25104         }
25105         
25106         return cfg;
25107         
25108     },
25109     
25110     /**
25111      * return the real input element.
25112      */
25113     inputEl: function ()
25114     {
25115         return this.el.select('input.roo-' + this.inputType,true).first();
25116     },
25117     hiddenEl: function ()
25118     {
25119         return this.el.select('input.roo-hidden-value',true).first();
25120     },
25121     
25122     labelEl: function()
25123     {
25124         return this.el.select('label.control-label',true).first();
25125     },
25126     /* depricated... */
25127     
25128     label: function()
25129     {
25130         return this.labelEl();
25131     },
25132     
25133     boxLabelEl: function()
25134     {
25135         return this.el.select('label.box-label',true).first();
25136     },
25137     
25138     initEvents : function()
25139     {
25140 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25141         
25142         this.inputEl().on('click', this.onClick,  this);
25143         
25144         if (this.boxLabel) { 
25145             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25146         }
25147         
25148         this.startValue = this.getValue();
25149         
25150         if(this.groupId){
25151             Roo.bootstrap.form.CheckBox.register(this);
25152         }
25153     },
25154     
25155     onClick : function(e)
25156     {   
25157         if(this.fireEvent('click', this, e) !== false){
25158             this.setChecked(!this.checked);
25159         }
25160         
25161     },
25162     
25163     setChecked : function(state,suppressEvent)
25164     {
25165         this.startValue = this.getValue();
25166
25167         if(this.inputType == 'radio'){
25168             
25169             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25170                 e.dom.checked = false;
25171             });
25172             
25173             this.inputEl().dom.checked = true;
25174             
25175             this.inputEl().dom.value = this.inputValue;
25176             
25177             if(suppressEvent !== true){
25178                 this.fireEvent('check', this, true);
25179             }
25180             
25181             this.validate();
25182             
25183             return;
25184         }
25185         
25186         this.checked = state;
25187         
25188         this.inputEl().dom.checked = state;
25189         
25190         
25191         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25192         
25193         if(suppressEvent !== true){
25194             this.fireEvent('check', this, state);
25195         }
25196         
25197         this.validate();
25198     },
25199     
25200     getValue : function()
25201     {
25202         if(this.inputType == 'radio'){
25203             return this.getGroupValue();
25204         }
25205         
25206         return this.hiddenEl().dom.value;
25207         
25208     },
25209     
25210     getGroupValue : function()
25211     {
25212         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25213             return '';
25214         }
25215         
25216         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25217     },
25218     
25219     setValue : function(v,suppressEvent)
25220     {
25221         if(this.inputType == 'radio'){
25222             this.setGroupValue(v, suppressEvent);
25223             return;
25224         }
25225         
25226         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25227         
25228         this.validate();
25229     },
25230     
25231     setGroupValue : function(v, suppressEvent)
25232     {
25233         this.startValue = this.getValue();
25234         
25235         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25236             e.dom.checked = false;
25237             
25238             if(e.dom.value == v){
25239                 e.dom.checked = true;
25240             }
25241         });
25242         
25243         if(suppressEvent !== true){
25244             this.fireEvent('check', this, true);
25245         }
25246
25247         this.validate();
25248         
25249         return;
25250     },
25251     
25252     validate : function()
25253     {
25254         if(this.getVisibilityEl().hasClass('hidden')){
25255             return true;
25256         }
25257         
25258         if(
25259                 this.disabled || 
25260                 (this.inputType == 'radio' && this.validateRadio()) ||
25261                 (this.inputType == 'checkbox' && this.validateCheckbox())
25262         ){
25263             this.markValid();
25264             return true;
25265         }
25266         
25267         this.markInvalid();
25268         return false;
25269     },
25270     
25271     validateRadio : function()
25272     {
25273         if(this.getVisibilityEl().hasClass('hidden')){
25274             return true;
25275         }
25276         
25277         if(this.allowBlank){
25278             return true;
25279         }
25280         
25281         var valid = false;
25282         
25283         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25284             if(!e.dom.checked){
25285                 return;
25286             }
25287             
25288             valid = true;
25289             
25290             return false;
25291         });
25292         
25293         return valid;
25294     },
25295     
25296     validateCheckbox : function()
25297     {
25298         if(!this.groupId){
25299             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25300             //return (this.getValue() == this.inputValue) ? true : false;
25301         }
25302         
25303         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25304         
25305         if(!group){
25306             return false;
25307         }
25308         
25309         var r = false;
25310         
25311         for(var i in group){
25312             if(group[i].el.isVisible(true)){
25313                 r = false;
25314                 break;
25315             }
25316             
25317             r = true;
25318         }
25319         
25320         for(var i in group){
25321             if(r){
25322                 break;
25323             }
25324             
25325             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25326         }
25327         
25328         return r;
25329     },
25330     
25331     /**
25332      * Mark this field as valid
25333      */
25334     markValid : function()
25335     {
25336         var _this = this;
25337         
25338         this.fireEvent('valid', this);
25339         
25340         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25341         
25342         if(this.groupId){
25343             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25344         }
25345         
25346         if(label){
25347             label.markValid();
25348         }
25349
25350         if(this.inputType == 'radio'){
25351             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25352                 var fg = e.findParent('.form-group', false, true);
25353                 if (Roo.bootstrap.version == 3) {
25354                     fg.removeClass([_this.invalidClass, _this.validClass]);
25355                     fg.addClass(_this.validClass);
25356                 } else {
25357                     fg.removeClass(['is-valid', 'is-invalid']);
25358                     fg.addClass('is-valid');
25359                 }
25360             });
25361             
25362             return;
25363         }
25364
25365         if(!this.groupId){
25366             var fg = this.el.findParent('.form-group', false, true);
25367             if (Roo.bootstrap.version == 3) {
25368                 fg.removeClass([this.invalidClass, this.validClass]);
25369                 fg.addClass(this.validClass);
25370             } else {
25371                 fg.removeClass(['is-valid', 'is-invalid']);
25372                 fg.addClass('is-valid');
25373             }
25374             return;
25375         }
25376         
25377         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25378         
25379         if(!group){
25380             return;
25381         }
25382         
25383         for(var i in group){
25384             var fg = group[i].el.findParent('.form-group', false, true);
25385             if (Roo.bootstrap.version == 3) {
25386                 fg.removeClass([this.invalidClass, this.validClass]);
25387                 fg.addClass(this.validClass);
25388             } else {
25389                 fg.removeClass(['is-valid', 'is-invalid']);
25390                 fg.addClass('is-valid');
25391             }
25392         }
25393     },
25394     
25395      /**
25396      * Mark this field as invalid
25397      * @param {String} msg The validation message
25398      */
25399     markInvalid : function(msg)
25400     {
25401         if(this.allowBlank){
25402             return;
25403         }
25404         
25405         var _this = this;
25406         
25407         this.fireEvent('invalid', this, msg);
25408         
25409         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25410         
25411         if(this.groupId){
25412             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25413         }
25414         
25415         if(label){
25416             label.markInvalid();
25417         }
25418             
25419         if(this.inputType == 'radio'){
25420             
25421             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25422                 var fg = e.findParent('.form-group', false, true);
25423                 if (Roo.bootstrap.version == 3) {
25424                     fg.removeClass([_this.invalidClass, _this.validClass]);
25425                     fg.addClass(_this.invalidClass);
25426                 } else {
25427                     fg.removeClass(['is-invalid', 'is-valid']);
25428                     fg.addClass('is-invalid');
25429                 }
25430             });
25431             
25432             return;
25433         }
25434         
25435         if(!this.groupId){
25436             var fg = this.el.findParent('.form-group', false, true);
25437             if (Roo.bootstrap.version == 3) {
25438                 fg.removeClass([_this.invalidClass, _this.validClass]);
25439                 fg.addClass(_this.invalidClass);
25440             } else {
25441                 fg.removeClass(['is-invalid', 'is-valid']);
25442                 fg.addClass('is-invalid');
25443             }
25444             return;
25445         }
25446         
25447         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25448         
25449         if(!group){
25450             return;
25451         }
25452         
25453         for(var i in group){
25454             var fg = group[i].el.findParent('.form-group', false, true);
25455             if (Roo.bootstrap.version == 3) {
25456                 fg.removeClass([_this.invalidClass, _this.validClass]);
25457                 fg.addClass(_this.invalidClass);
25458             } else {
25459                 fg.removeClass(['is-invalid', 'is-valid']);
25460                 fg.addClass('is-invalid');
25461             }
25462         }
25463         
25464     },
25465     
25466     clearInvalid : function()
25467     {
25468         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25469         
25470         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25471         
25472         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25473         
25474         if (label && label.iconEl) {
25475             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25476             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25477         }
25478     },
25479     
25480     disable : function()
25481     {
25482         if(this.inputType != 'radio'){
25483             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25484             return;
25485         }
25486         
25487         var _this = this;
25488         
25489         if(this.rendered){
25490             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25491                 _this.getActionEl().addClass(this.disabledClass);
25492                 e.dom.disabled = true;
25493             });
25494         }
25495         
25496         this.disabled = true;
25497         this.fireEvent("disable", this);
25498         return this;
25499     },
25500
25501     enable : function()
25502     {
25503         if(this.inputType != 'radio'){
25504             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25505             return;
25506         }
25507         
25508         var _this = this;
25509         
25510         if(this.rendered){
25511             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25512                 _this.getActionEl().removeClass(this.disabledClass);
25513                 e.dom.disabled = false;
25514             });
25515         }
25516         
25517         this.disabled = false;
25518         this.fireEvent("enable", this);
25519         return this;
25520     },
25521     
25522     setBoxLabel : function(v)
25523     {
25524         this.boxLabel = v;
25525         
25526         if(this.rendered){
25527             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25528         }
25529     }
25530
25531 });
25532
25533 Roo.apply(Roo.bootstrap.form.CheckBox, {
25534     
25535     groups: {},
25536     
25537      /**
25538     * register a CheckBox Group
25539     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25540     */
25541     register : function(checkbox)
25542     {
25543         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25544             this.groups[checkbox.groupId] = {};
25545         }
25546         
25547         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25548             return;
25549         }
25550         
25551         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25552         
25553     },
25554     /**
25555     * fetch a CheckBox Group based on the group ID
25556     * @param {string} the group ID
25557     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25558     */
25559     get: function(groupId) {
25560         if (typeof(this.groups[groupId]) == 'undefined') {
25561             return false;
25562         }
25563         
25564         return this.groups[groupId] ;
25565     }
25566     
25567     
25568 });
25569 /*
25570  * - LGPL
25571  *
25572  * RadioItem
25573  * 
25574  */
25575
25576 /**
25577  * @class Roo.bootstrap.form.Radio
25578  * @extends Roo.bootstrap.Component
25579  * Bootstrap Radio class
25580  * @cfg {String} boxLabel - the label associated
25581  * @cfg {String} value - the value of radio
25582  * 
25583  * @constructor
25584  * Create a new Radio
25585  * @param {Object} config The config object
25586  */
25587 Roo.bootstrap.form.Radio = function(config){
25588     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25589     
25590 };
25591
25592 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25593     
25594     boxLabel : '',
25595     
25596     value : '',
25597     
25598     getAutoCreate : function()
25599     {
25600         var cfg = {
25601             tag : 'div',
25602             cls : 'form-group radio',
25603             cn : [
25604                 {
25605                     tag : 'label',
25606                     cls : 'box-label',
25607                     html : this.boxLabel
25608                 }
25609             ]
25610         };
25611         
25612         return cfg;
25613     },
25614     
25615     initEvents : function() 
25616     {
25617         this.parent().register(this);
25618         
25619         this.el.on('click', this.onClick, this);
25620         
25621     },
25622     
25623     onClick : function(e)
25624     {
25625         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25626             this.setChecked(true);
25627         }
25628     },
25629     
25630     setChecked : function(state, suppressEvent)
25631     {
25632         this.parent().setValue(this.value, suppressEvent);
25633         
25634     },
25635     
25636     setBoxLabel : function(v)
25637     {
25638         this.boxLabel = v;
25639         
25640         if(this.rendered){
25641             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25642         }
25643     }
25644     
25645 });
25646  
25647
25648  /*
25649  * - LGPL
25650  *
25651  * Input
25652  * 
25653  */
25654
25655 /**
25656  * @class Roo.bootstrap.form.SecurePass
25657  * @extends Roo.bootstrap.form.Input
25658  * Bootstrap SecurePass class
25659  *
25660  * 
25661  * @constructor
25662  * Create a new SecurePass
25663  * @param {Object} config The config object
25664  */
25665  
25666 Roo.bootstrap.form.SecurePass = function (config) {
25667     // these go here, so the translation tool can replace them..
25668     this.errors = {
25669         PwdEmpty: "Please type a password, and then retype it to confirm.",
25670         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25671         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25672         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25673         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25674         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25675         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25676         TooWeak: "Your password is Too Weak."
25677     },
25678     this.meterLabel = "Password strength:";
25679     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25680     this.meterClass = [
25681         "roo-password-meter-tooweak", 
25682         "roo-password-meter-weak", 
25683         "roo-password-meter-medium", 
25684         "roo-password-meter-strong", 
25685         "roo-password-meter-grey"
25686     ];
25687     
25688     this.errors = {};
25689     
25690     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25691 }
25692
25693 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25694     /**
25695      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25696      * {
25697      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25698      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25699      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25700      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25701      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25702      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25703      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25704      * })
25705      */
25706     // private
25707     
25708     meterWidth: 300,
25709     errorMsg :'',    
25710     errors: false,
25711     imageRoot: '/',
25712     /**
25713      * @cfg {String/Object} Label for the strength meter (defaults to
25714      * 'Password strength:')
25715      */
25716     // private
25717     meterLabel: '',
25718     /**
25719      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25720      * ['Weak', 'Medium', 'Strong'])
25721      */
25722     // private    
25723     pwdStrengths: false,    
25724     // private
25725     strength: 0,
25726     // private
25727     _lastPwd: null,
25728     // private
25729     kCapitalLetter: 0,
25730     kSmallLetter: 1,
25731     kDigit: 2,
25732     kPunctuation: 3,
25733     
25734     insecure: false,
25735     // private
25736     initEvents: function ()
25737     {
25738         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25739
25740         if (this.el.is('input[type=password]') && Roo.isSafari) {
25741             this.el.on('keydown', this.SafariOnKeyDown, this);
25742         }
25743
25744         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25745     },
25746     // private
25747     onRender: function (ct, position)
25748     {
25749         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25750         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25751         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25752
25753         this.trigger.createChild({
25754                    cn: [
25755                     {
25756                     //id: 'PwdMeter',
25757                     tag: 'div',
25758                     cls: 'roo-password-meter-grey col-xs-12',
25759                     style: {
25760                         //width: 0,
25761                         //width: this.meterWidth + 'px'                                                
25762                         }
25763                     },
25764                     {                            
25765                          cls: 'roo-password-meter-text'                          
25766                     }
25767                 ]            
25768         });
25769
25770          
25771         if (this.hideTrigger) {
25772             this.trigger.setDisplayed(false);
25773         }
25774         this.setSize(this.width || '', this.height || '');
25775     },
25776     // private
25777     onDestroy: function ()
25778     {
25779         if (this.trigger) {
25780             this.trigger.removeAllListeners();
25781             this.trigger.remove();
25782         }
25783         if (this.wrap) {
25784             this.wrap.remove();
25785         }
25786         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25787     },
25788     // private
25789     checkStrength: function ()
25790     {
25791         var pwd = this.inputEl().getValue();
25792         if (pwd == this._lastPwd) {
25793             return;
25794         }
25795
25796         var strength;
25797         if (this.ClientSideStrongPassword(pwd)) {
25798             strength = 3;
25799         } else if (this.ClientSideMediumPassword(pwd)) {
25800             strength = 2;
25801         } else if (this.ClientSideWeakPassword(pwd)) {
25802             strength = 1;
25803         } else {
25804             strength = 0;
25805         }
25806         
25807         Roo.log('strength1: ' + strength);
25808         
25809         //var pm = this.trigger.child('div/div/div').dom;
25810         var pm = this.trigger.child('div/div');
25811         pm.removeClass(this.meterClass);
25812         pm.addClass(this.meterClass[strength]);
25813                 
25814         
25815         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25816                 
25817         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25818         
25819         this._lastPwd = pwd;
25820     },
25821     reset: function ()
25822     {
25823         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25824         
25825         this._lastPwd = '';
25826         
25827         var pm = this.trigger.child('div/div');
25828         pm.removeClass(this.meterClass);
25829         pm.addClass('roo-password-meter-grey');        
25830         
25831         
25832         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25833         
25834         pt.innerHTML = '';
25835         this.inputEl().dom.type='password';
25836     },
25837     // private
25838     validateValue: function (value)
25839     {
25840         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25841             return false;
25842         }
25843         if (value.length == 0) {
25844             if (this.allowBlank) {
25845                 this.clearInvalid();
25846                 return true;
25847             }
25848
25849             this.markInvalid(this.errors.PwdEmpty);
25850             this.errorMsg = this.errors.PwdEmpty;
25851             return false;
25852         }
25853         
25854         if(this.insecure){
25855             return true;
25856         }
25857         
25858         if (!value.match(/[\x21-\x7e]+/)) {
25859             this.markInvalid(this.errors.PwdBadChar);
25860             this.errorMsg = this.errors.PwdBadChar;
25861             return false;
25862         }
25863         if (value.length < 6) {
25864             this.markInvalid(this.errors.PwdShort);
25865             this.errorMsg = this.errors.PwdShort;
25866             return false;
25867         }
25868         if (value.length > 16) {
25869             this.markInvalid(this.errors.PwdLong);
25870             this.errorMsg = this.errors.PwdLong;
25871             return false;
25872         }
25873         var strength;
25874         if (this.ClientSideStrongPassword(value)) {
25875             strength = 3;
25876         } else if (this.ClientSideMediumPassword(value)) {
25877             strength = 2;
25878         } else if (this.ClientSideWeakPassword(value)) {
25879             strength = 1;
25880         } else {
25881             strength = 0;
25882         }
25883
25884         
25885         if (strength < 2) {
25886             //this.markInvalid(this.errors.TooWeak);
25887             this.errorMsg = this.errors.TooWeak;
25888             //return false;
25889         }
25890         
25891         
25892         console.log('strength2: ' + strength);
25893         
25894         //var pm = this.trigger.child('div/div/div').dom;
25895         
25896         var pm = this.trigger.child('div/div');
25897         pm.removeClass(this.meterClass);
25898         pm.addClass(this.meterClass[strength]);
25899                 
25900         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25901                 
25902         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25903         
25904         this.errorMsg = ''; 
25905         return true;
25906     },
25907     // private
25908     CharacterSetChecks: function (type)
25909     {
25910         this.type = type;
25911         this.fResult = false;
25912     },
25913     // private
25914     isctype: function (character, type)
25915     {
25916         switch (type) {  
25917             case this.kCapitalLetter:
25918                 if (character >= 'A' && character <= 'Z') {
25919                     return true;
25920                 }
25921                 break;
25922             
25923             case this.kSmallLetter:
25924                 if (character >= 'a' && character <= 'z') {
25925                     return true;
25926                 }
25927                 break;
25928             
25929             case this.kDigit:
25930                 if (character >= '0' && character <= '9') {
25931                     return true;
25932                 }
25933                 break;
25934             
25935             case this.kPunctuation:
25936                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25937                     return true;
25938                 }
25939                 break;
25940             
25941             default:
25942                 return false;
25943         }
25944
25945     },
25946     // private
25947     IsLongEnough: function (pwd, size)
25948     {
25949         return !(pwd == null || isNaN(size) || pwd.length < size);
25950     },
25951     // private
25952     SpansEnoughCharacterSets: function (word, nb)
25953     {
25954         if (!this.IsLongEnough(word, nb))
25955         {
25956             return false;
25957         }
25958
25959         var characterSetChecks = new Array(
25960             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25961             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25962         );
25963         
25964         for (var index = 0; index < word.length; ++index) {
25965             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25966                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25967                     characterSetChecks[nCharSet].fResult = true;
25968                     break;
25969                 }
25970             }
25971         }
25972
25973         var nCharSets = 0;
25974         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25975             if (characterSetChecks[nCharSet].fResult) {
25976                 ++nCharSets;
25977             }
25978         }
25979
25980         if (nCharSets < nb) {
25981             return false;
25982         }
25983         return true;
25984     },
25985     // private
25986     ClientSideStrongPassword: function (pwd)
25987     {
25988         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25989     },
25990     // private
25991     ClientSideMediumPassword: function (pwd)
25992     {
25993         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25994     },
25995     // private
25996     ClientSideWeakPassword: function (pwd)
25997     {
25998         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25999     }
26000           
26001 });
26002 Roo.htmleditor = {};
26003  
26004 /**
26005  * @class Roo.htmleditor.Filter
26006  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26007  * @cfg {DomElement} node The node to iterate and filter
26008  * @cfg {boolean|String|Array} tag Tags to replace 
26009  * @constructor
26010  * Create a new Filter.
26011  * @param {Object} config Configuration options
26012  */
26013
26014
26015
26016 Roo.htmleditor.Filter = function(cfg) {
26017     Roo.apply(this.cfg);
26018     // this does not actually call walk as it's really just a abstract class
26019 }
26020
26021
26022 Roo.htmleditor.Filter.prototype = {
26023     
26024     node: false,
26025     
26026     tag: false,
26027
26028     // overrride to do replace comments.
26029     replaceComment : false,
26030     
26031     // overrride to do replace or do stuff with tags..
26032     replaceTag : false,
26033     
26034     walk : function(dom)
26035     {
26036         Roo.each( Array.from(dom.childNodes), function( e ) {
26037             switch(true) {
26038                 
26039                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26040                     this.replaceComment(e);
26041                     return;
26042                 
26043                 case e.nodeType != 1: //not a node.
26044                     return;
26045                 
26046                 case this.tag === true: // everything
26047                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26048                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26049                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26050                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26051                     if (this.replaceTag && false === this.replaceTag(e)) {
26052                         return;
26053                     }
26054                     if (e.hasChildNodes()) {
26055                         this.walk(e);
26056                     }
26057                     return;
26058                 
26059                 default:    // tags .. that do not match.
26060                     if (e.hasChildNodes()) {
26061                         this.walk(e);
26062                     }
26063             }
26064             
26065         }, this);
26066         
26067     },
26068     
26069     
26070     removeNodeKeepChildren : function( node)
26071     {
26072     
26073         ar = Array.from(node.childNodes);
26074         for (var i = 0; i < ar.length; i++) {
26075          
26076             node.removeChild(ar[i]);
26077             // what if we need to walk these???
26078             node.parentNode.insertBefore(ar[i], node);
26079            
26080         }
26081         node.parentNode.removeChild(node);
26082     }
26083 }; 
26084
26085 /**
26086  * @class Roo.htmleditor.FilterAttributes
26087  * clean attributes and  styles including http:// etc.. in attribute
26088  * @constructor
26089 * Run a new Attribute Filter
26090 * @param {Object} config Configuration options
26091  */
26092 Roo.htmleditor.FilterAttributes = function(cfg)
26093 {
26094     Roo.apply(this, cfg);
26095     this.attrib_black = this.attrib_black || [];
26096     this.attrib_white = this.attrib_white || [];
26097
26098     this.attrib_clean = this.attrib_clean || [];
26099     this.style_white = this.style_white || [];
26100     this.style_black = this.style_black || [];
26101     this.walk(cfg.node);
26102 }
26103
26104 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26105 {
26106     tag: true, // all tags
26107     
26108     attrib_black : false, // array
26109     attrib_clean : false,
26110     attrib_white : false,
26111
26112     style_white : false,
26113     style_black : false,
26114      
26115      
26116     replaceTag : function(node)
26117     {
26118         if (!node.attributes || !node.attributes.length) {
26119             return true;
26120         }
26121         
26122         for (var i = node.attributes.length-1; i > -1 ; i--) {
26123             var a = node.attributes[i];
26124             //console.log(a);
26125             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26126                 node.removeAttribute(a.name);
26127                 continue;
26128             }
26129             
26130             
26131             
26132             if (a.name.toLowerCase().substr(0,2)=='on')  {
26133                 node.removeAttribute(a.name);
26134                 continue;
26135             }
26136             
26137             
26138             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26139                 node.removeAttribute(a.name);
26140                 continue;
26141             }
26142             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26143                 this.cleanAttr(node,a.name,a.value); // fixme..
26144                 continue;
26145             }
26146             if (a.name == 'style') {
26147                 this.cleanStyle(node,a.name,a.value);
26148                 continue;
26149             }
26150             /// clean up MS crap..
26151             // tecnically this should be a list of valid class'es..
26152             
26153             
26154             if (a.name == 'class') {
26155                 if (a.value.match(/^Mso/)) {
26156                     node.removeAttribute('class');
26157                 }
26158                 
26159                 if (a.value.match(/^body$/)) {
26160                     node.removeAttribute('class');
26161                 }
26162                 continue;
26163             }
26164             
26165             
26166             // style cleanup!?
26167             // class cleanup?
26168             
26169         }
26170         return true; // clean children
26171     },
26172         
26173     cleanAttr: function(node, n,v)
26174     {
26175         
26176         if (v.match(/^\./) || v.match(/^\//)) {
26177             return;
26178         }
26179         if (v.match(/^(http|https):\/\//)
26180             || v.match(/^mailto:/) 
26181             || v.match(/^ftp:/)
26182             || v.match(/^data:/)
26183             ) {
26184             return;
26185         }
26186         if (v.match(/^#/)) {
26187             return;
26188         }
26189         if (v.match(/^\{/)) { // allow template editing.
26190             return;
26191         }
26192 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26193         node.removeAttribute(n);
26194         
26195     },
26196     cleanStyle : function(node,  n,v)
26197     {
26198         if (v.match(/expression/)) { //XSS?? should we even bother..
26199             node.removeAttribute(n);
26200             return;
26201         }
26202         
26203         var parts = v.split(/;/);
26204         var clean = [];
26205         
26206         Roo.each(parts, function(p) {
26207             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26208             if (!p.length) {
26209                 return true;
26210             }
26211             var l = p.split(':').shift().replace(/\s+/g,'');
26212             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26213             
26214             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26215                 return true;
26216             }
26217             //Roo.log()
26218             // only allow 'c whitelisted system attributes'
26219             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26220                 return true;
26221             }
26222             
26223             
26224             clean.push(p);
26225             return true;
26226         },this);
26227         if (clean.length) { 
26228             node.setAttribute(n, clean.join(';'));
26229         } else {
26230             node.removeAttribute(n);
26231         }
26232         
26233     }
26234         
26235         
26236         
26237     
26238 });/**
26239  * @class Roo.htmleditor.FilterBlack
26240  * remove blacklisted elements.
26241  * @constructor
26242  * Run a new Blacklisted Filter
26243  * @param {Object} config Configuration options
26244  */
26245
26246 Roo.htmleditor.FilterBlack = function(cfg)
26247 {
26248     Roo.apply(this, cfg);
26249     this.walk(cfg.node);
26250 }
26251
26252 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26253 {
26254     tag : true, // all elements.
26255    
26256     replaceTag : function(n)
26257     {
26258         n.parentNode.removeChild(n);
26259     }
26260 });
26261 /**
26262  * @class Roo.htmleditor.FilterComment
26263  * remove comments.
26264  * @constructor
26265 * Run a new Comments Filter
26266 * @param {Object} config Configuration options
26267  */
26268 Roo.htmleditor.FilterComment = function(cfg)
26269 {
26270     this.walk(cfg.node);
26271 }
26272
26273 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26274 {
26275   
26276     replaceComment : function(n)
26277     {
26278         n.parentNode.removeChild(n);
26279     }
26280 });/**
26281  * @class Roo.htmleditor.FilterKeepChildren
26282  * remove tags but keep children
26283  * @constructor
26284  * Run a new Keep Children Filter
26285  * @param {Object} config Configuration options
26286  */
26287
26288 Roo.htmleditor.FilterKeepChildren = function(cfg)
26289 {
26290     Roo.apply(this, cfg);
26291     if (this.tag === false) {
26292         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26293     }
26294     // hacky?
26295     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26296         this.cleanNamespace = true;
26297     }
26298         
26299     this.walk(cfg.node);
26300 }
26301
26302 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26303 {
26304     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26305   
26306     replaceTag : function(node)
26307     {
26308         // walk children...
26309         //Roo.log(node.tagName);
26310         var ar = Array.from(node.childNodes);
26311         //remove first..
26312         
26313         for (var i = 0; i < ar.length; i++) {
26314             var e = ar[i];
26315             if (e.nodeType == 1) {
26316                 if (
26317                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26318                     || // array and it matches
26319                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26320                     ||
26321                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26322                     ||
26323                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26324                 ) {
26325                     this.replaceTag(ar[i]); // child is blacklisted as well...
26326                     continue;
26327                 }
26328             }
26329         }  
26330         ar = Array.from(node.childNodes);
26331         for (var i = 0; i < ar.length; i++) {
26332          
26333             node.removeChild(ar[i]);
26334             // what if we need to walk these???
26335             node.parentNode.insertBefore(ar[i], node);
26336             if (this.tag !== false) {
26337                 this.walk(ar[i]);
26338                 
26339             }
26340         }
26341         //Roo.log("REMOVE:" + node.tagName);
26342         node.parentNode.removeChild(node);
26343         return false; // don't walk children
26344         
26345         
26346     }
26347 });/**
26348  * @class Roo.htmleditor.FilterParagraph
26349  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26350  * like on 'push' to remove the <p> tags and replace them with line breaks.
26351  * @constructor
26352  * Run a new Paragraph Filter
26353  * @param {Object} config Configuration options
26354  */
26355
26356 Roo.htmleditor.FilterParagraph = function(cfg)
26357 {
26358     // no need to apply config.
26359     this.walk(cfg.node);
26360 }
26361
26362 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26363 {
26364     
26365      
26366     tag : 'P',
26367     
26368      
26369     replaceTag : function(node)
26370     {
26371         
26372         if (node.childNodes.length == 1 &&
26373             node.childNodes[0].nodeType == 3 &&
26374             node.childNodes[0].textContent.trim().length < 1
26375             ) {
26376             // remove and replace with '<BR>';
26377             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26378             return false; // no need to walk..
26379         }
26380         var ar = Array.from(node.childNodes);
26381         for (var i = 0; i < ar.length; i++) {
26382             node.removeChild(ar[i]);
26383             // what if we need to walk these???
26384             node.parentNode.insertBefore(ar[i], node);
26385         }
26386         // now what about this?
26387         // <p> &nbsp; </p>
26388         
26389         // double BR.
26390         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26391         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26392         node.parentNode.removeChild(node);
26393         
26394         return false;
26395
26396     }
26397     
26398 });/**
26399  * @class Roo.htmleditor.FilterSpan
26400  * filter span's with no attributes out..
26401  * @constructor
26402  * Run a new Span Filter
26403  * @param {Object} config Configuration options
26404  */
26405
26406 Roo.htmleditor.FilterSpan = function(cfg)
26407 {
26408     // no need to apply config.
26409     this.walk(cfg.node);
26410 }
26411
26412 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26413 {
26414      
26415     tag : 'SPAN',
26416      
26417  
26418     replaceTag : function(node)
26419     {
26420         if (node.attributes && node.attributes.length > 0) {
26421             return true; // walk if there are any.
26422         }
26423         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26424         return false;
26425      
26426     }
26427     
26428 });/**
26429  * @class Roo.htmleditor.FilterTableWidth
26430   try and remove table width data - as that frequently messes up other stuff.
26431  * 
26432  *      was cleanTableWidths.
26433  *
26434  * Quite often pasting from word etc.. results in tables with column and widths.
26435  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26436  *
26437  * @constructor
26438  * Run a new Table Filter
26439  * @param {Object} config Configuration options
26440  */
26441
26442 Roo.htmleditor.FilterTableWidth = function(cfg)
26443 {
26444     // no need to apply config.
26445     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26446     this.walk(cfg.node);
26447 }
26448
26449 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26450 {
26451      
26452      
26453     
26454     replaceTag: function(node) {
26455         
26456         
26457       
26458         if (node.hasAttribute('width')) {
26459             node.removeAttribute('width');
26460         }
26461         
26462          
26463         if (node.hasAttribute("style")) {
26464             // pretty basic...
26465             
26466             var styles = node.getAttribute("style").split(";");
26467             var nstyle = [];
26468             Roo.each(styles, function(s) {
26469                 if (!s.match(/:/)) {
26470                     return;
26471                 }
26472                 var kv = s.split(":");
26473                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26474                     return;
26475                 }
26476                 // what ever is left... we allow.
26477                 nstyle.push(s);
26478             });
26479             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26480             if (!nstyle.length) {
26481                 node.removeAttribute('style');
26482             }
26483         }
26484         
26485         return true; // continue doing children..
26486     }
26487 });/**
26488  * @class Roo.htmleditor.FilterWord
26489  * try and clean up all the mess that Word generates.
26490  * 
26491  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26492  
26493  * @constructor
26494  * Run a new Span Filter
26495  * @param {Object} config Configuration options
26496  */
26497
26498 Roo.htmleditor.FilterWord = function(cfg)
26499 {
26500     // no need to apply config.
26501     this.replaceDocBullets(cfg.node);
26502     
26503     this.replaceAname(cfg.node);
26504     // this is disabled as the removal is done by other filters;
26505    // this.walk(cfg.node);
26506     
26507     
26508 }
26509
26510 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26511 {
26512     tag: true,
26513      
26514     
26515     /**
26516      * Clean up MS wordisms...
26517      */
26518     replaceTag : function(node)
26519     {
26520          
26521         // no idea what this does - span with text, replaceds with just text.
26522         if(
26523                 node.nodeName == 'SPAN' &&
26524                 !node.hasAttributes() &&
26525                 node.childNodes.length == 1 &&
26526                 node.firstChild.nodeName == "#text"  
26527         ) {
26528             var textNode = node.firstChild;
26529             node.removeChild(textNode);
26530             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26531                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26532             }
26533             node.parentNode.insertBefore(textNode, node);
26534             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26535                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26536             }
26537             
26538             node.parentNode.removeChild(node);
26539             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26540         }
26541         
26542    
26543         
26544         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26545             node.parentNode.removeChild(node);
26546             return false; // dont do chidlren
26547         }
26548         //Roo.log(node.tagName);
26549         // remove - but keep children..
26550         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26551             //Roo.log('-- removed');
26552             while (node.childNodes.length) {
26553                 var cn = node.childNodes[0];
26554                 node.removeChild(cn);
26555                 node.parentNode.insertBefore(cn, node);
26556                 // move node to parent - and clean it..
26557                 if (cn.nodeType == 1) {
26558                     this.replaceTag(cn);
26559                 }
26560                 
26561             }
26562             node.parentNode.removeChild(node);
26563             /// no need to iterate chidlren = it's got none..
26564             //this.iterateChildren(node, this.cleanWord);
26565             return false; // no need to iterate children.
26566         }
26567         // clean styles
26568         if (node.className.length) {
26569             
26570             var cn = node.className.split(/\W+/);
26571             var cna = [];
26572             Roo.each(cn, function(cls) {
26573                 if (cls.match(/Mso[a-zA-Z]+/)) {
26574                     return;
26575                 }
26576                 cna.push(cls);
26577             });
26578             node.className = cna.length ? cna.join(' ') : '';
26579             if (!cna.length) {
26580                 node.removeAttribute("class");
26581             }
26582         }
26583         
26584         if (node.hasAttribute("lang")) {
26585             node.removeAttribute("lang");
26586         }
26587         
26588         if (node.hasAttribute("style")) {
26589             
26590             var styles = node.getAttribute("style").split(";");
26591             var nstyle = [];
26592             Roo.each(styles, function(s) {
26593                 if (!s.match(/:/)) {
26594                     return;
26595                 }
26596                 var kv = s.split(":");
26597                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26598                     return;
26599                 }
26600                 // what ever is left... we allow.
26601                 nstyle.push(s);
26602             });
26603             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26604             if (!nstyle.length) {
26605                 node.removeAttribute('style');
26606             }
26607         }
26608         return true; // do children
26609         
26610         
26611         
26612     },
26613     
26614     styleToObject: function(node)
26615     {
26616         var styles = (node.getAttribute("style") || '').split(";");
26617         var ret = {};
26618         Roo.each(styles, function(s) {
26619             if (!s.match(/:/)) {
26620                 return;
26621             }
26622             var kv = s.split(":");
26623              
26624             // what ever is left... we allow.
26625             ret[kv[0].trim()] = kv[1];
26626         });
26627         return ret;
26628     },
26629     
26630     
26631     replaceAname : function (doc)
26632     {
26633         // replace all the a/name without..
26634         var aa = Array.from(doc.getElementsByTagName('a'));
26635         for (var i = 0; i  < aa.length; i++) {
26636             var a = aa[i];
26637             if (a.hasAttribute("name")) {
26638                 a.removeAttribute("name");
26639             }
26640             if (a.hasAttribute("href")) {
26641                 continue;
26642             }
26643             // reparent children.
26644             this.removeNodeKeepChildren(a);
26645             
26646         }
26647         
26648         
26649         
26650     },
26651
26652     
26653     
26654     replaceDocBullets : function(doc)
26655     {
26656         // this is a bit odd - but it appears some indents use ql-indent-1
26657          //Roo.log(doc.innerHTML);
26658         
26659         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26660         for( var i = 0; i < listpara.length; i ++) {
26661             listpara[i].className = "MsoListParagraph";
26662         }
26663         
26664         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26665         for( var i = 0; i < listpara.length; i ++) {
26666             listpara[i].className = "MsoListParagraph";
26667         }
26668         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26669         for( var i = 0; i < listpara.length; i ++) {
26670             listpara[i].className = "MsoListParagraph";
26671         }
26672         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26673         for( var i = 0; i < listpara.length; i ++) {
26674             listpara[i].className = "MsoListParagraph";
26675         }
26676         
26677         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26678         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26679         for( var i = 0; i < htwo.length; i ++) {
26680             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26681                 htwo[i].className = "MsoListParagraph";
26682             }
26683         }
26684         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26685         for( var i = 0; i < listpara.length; i ++) {
26686             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26687                 listpara[i].className = "MsoListParagraph";
26688             } else {
26689                 listpara[i].className = "MsoNormalx";
26690             }
26691         }
26692        
26693         listpara = doc.getElementsByClassName('MsoListParagraph');
26694         // Roo.log(doc.innerHTML);
26695         
26696         
26697         
26698         while(listpara.length) {
26699             
26700             this.replaceDocBullet(listpara.item(0));
26701         }
26702       
26703     },
26704     
26705      
26706     
26707     replaceDocBullet : function(p)
26708     {
26709         // gather all the siblings.
26710         var ns = p,
26711             parent = p.parentNode,
26712             doc = parent.ownerDocument,
26713             items = [];
26714             
26715         var listtype = 'ul';   
26716         while (ns) {
26717             if (ns.nodeType != 1) {
26718                 ns = ns.nextSibling;
26719                 continue;
26720             }
26721             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26722                 break;
26723             }
26724             var spans = ns.getElementsByTagName('span');
26725             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26726                 items.push(ns);
26727                 ns = ns.nextSibling;
26728                 has_list = true;
26729                 if (spans.length && spans[0].hasAttribute('style')) {
26730                     var  style = this.styleToObject(spans[0]);
26731                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26732                         listtype = 'ol';
26733                     }
26734                 }
26735                 
26736                 continue;
26737             }
26738             var spans = ns.getElementsByTagName('span');
26739             if (!spans.length) {
26740                 break;
26741             }
26742             var has_list  = false;
26743             for(var i = 0; i < spans.length; i++) {
26744                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26745                     has_list = true;
26746                     break;
26747                 }
26748             }
26749             if (!has_list) {
26750                 break;
26751             }
26752             items.push(ns);
26753             ns = ns.nextSibling;
26754             
26755             
26756         }
26757         if (!items.length) {
26758             ns.className = "";
26759             return;
26760         }
26761         
26762         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26763         parent.insertBefore(ul, p);
26764         var lvl = 0;
26765         var stack = [ ul ];
26766         var last_li = false;
26767         
26768         var margin_to_depth = {};
26769         max_margins = -1;
26770         
26771         items.forEach(function(n, ipos) {
26772             //Roo.log("got innertHMLT=" + n.innerHTML);
26773             
26774             var spans = n.getElementsByTagName('span');
26775             if (!spans.length) {
26776                 //Roo.log("No spans found");
26777                  
26778                 parent.removeChild(n);
26779                 
26780                 
26781                 return; // skip it...
26782             }
26783            
26784                 
26785             var num = 1;
26786             var style = {};
26787             for(var i = 0; i < spans.length; i++) {
26788             
26789                 style = this.styleToObject(spans[i]);
26790                 if (typeof(style['mso-list']) == 'undefined') {
26791                     continue;
26792                 }
26793                 if (listtype == 'ol') {
26794                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26795                 }
26796                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26797                 break;
26798             }
26799             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26800             style = this.styleToObject(n); // mo-list is from the parent node.
26801             if (typeof(style['mso-list']) == 'undefined') {
26802                 //Roo.log("parent is missing level");
26803                   
26804                 parent.removeChild(n);
26805                  
26806                 return;
26807             }
26808             
26809             var margin = style['margin-left'];
26810             if (typeof(margin_to_depth[margin]) == 'undefined') {
26811                 max_margins++;
26812                 margin_to_depth[margin] = max_margins;
26813             }
26814             nlvl = margin_to_depth[margin] ;
26815              
26816             if (nlvl > lvl) {
26817                 //new indent
26818                 var nul = doc.createElement(listtype); // what about number lists...
26819                 if (!last_li) {
26820                     last_li = doc.createElement('li');
26821                     stack[lvl].appendChild(last_li);
26822                 }
26823                 last_li.appendChild(nul);
26824                 stack[nlvl] = nul;
26825                 
26826             }
26827             lvl = nlvl;
26828             
26829             // not starting at 1..
26830             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26831                 stack[nlvl].setAttribute("start", num);
26832             }
26833             
26834             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26835             last_li = nli;
26836             nli.innerHTML = n.innerHTML;
26837             //Roo.log("innerHTML = " + n.innerHTML);
26838             parent.removeChild(n);
26839             
26840              
26841              
26842             
26843         },this);
26844         
26845         
26846         
26847         
26848     }
26849     
26850     
26851     
26852 });
26853 /**
26854  * @class Roo.htmleditor.FilterStyleToTag
26855  * part of the word stuff... - certain 'styles' should be converted to tags.
26856  * eg.
26857  *   font-weight: bold -> bold
26858  *   ?? super / subscrit etc..
26859  * 
26860  * @constructor
26861 * Run a new style to tag filter.
26862 * @param {Object} config Configuration options
26863  */
26864 Roo.htmleditor.FilterStyleToTag = function(cfg)
26865 {
26866     
26867     this.tags = {
26868         B  : [ 'fontWeight' , 'bold'],
26869         I :  [ 'fontStyle' , 'italic'],
26870         //pre :  [ 'font-style' , 'italic'],
26871         // h1.. h6 ?? font-size?
26872         SUP : [ 'verticalAlign' , 'super' ],
26873         SUB : [ 'verticalAlign' , 'sub' ]
26874         
26875         
26876     };
26877     
26878     Roo.apply(this, cfg);
26879      
26880     
26881     this.walk(cfg.node);
26882     
26883     
26884     
26885 }
26886
26887
26888 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26889 {
26890     tag: true, // all tags
26891     
26892     tags : false,
26893     
26894     
26895     replaceTag : function(node)
26896     {
26897         
26898         
26899         if (node.getAttribute("style") === null) {
26900             return true;
26901         }
26902         var inject = [];
26903         for (var k in this.tags) {
26904             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26905                 inject.push(k);
26906                 node.style.removeProperty(this.tags[k][0]);
26907             }
26908         }
26909         if (!inject.length) {
26910             return true; 
26911         }
26912         var cn = Array.from(node.childNodes);
26913         var nn = node;
26914         Roo.each(inject, function(t) {
26915             var nc = node.ownerDocument.createElement(t);
26916             nn.appendChild(nc);
26917             nn = nc;
26918         });
26919         for(var i = 0;i < cn.length;cn++) {
26920             node.removeChild(cn[i]);
26921             nn.appendChild(cn[i]);
26922         }
26923         return true /// iterate thru
26924     }
26925     
26926 })/**
26927  * @class Roo.htmleditor.FilterLongBr
26928  * BR/BR/BR - keep a maximum of 2...
26929  * @constructor
26930  * Run a new Long BR Filter
26931  * @param {Object} config Configuration options
26932  */
26933
26934 Roo.htmleditor.FilterLongBr = function(cfg)
26935 {
26936     // no need to apply config.
26937     this.walk(cfg.node);
26938 }
26939
26940 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26941 {
26942     
26943      
26944     tag : 'BR',
26945     
26946      
26947     replaceTag : function(node)
26948     {
26949         
26950         var ps = node.nextSibling;
26951         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26952             ps = ps.nextSibling;
26953         }
26954         
26955         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26956             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26957             return false;
26958         }
26959         
26960         if (!ps || ps.nodeType != 1) {
26961             return false;
26962         }
26963         
26964         if (!ps || ps.tagName != 'BR') {
26965            
26966             return false;
26967         }
26968         
26969         
26970         
26971         
26972         
26973         if (!node.previousSibling) {
26974             return false;
26975         }
26976         var ps = node.previousSibling;
26977         
26978         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26979             ps = ps.previousSibling;
26980         }
26981         if (!ps || ps.nodeType != 1) {
26982             return false;
26983         }
26984         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26985         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26986             return false;
26987         }
26988         
26989         node.parentNode.removeChild(node); // remove me...
26990         
26991         return false; // no need to do children
26992
26993     }
26994     
26995 }); 
26996
26997 /**
26998  * @class Roo.htmleditor.FilterBlock
26999  * removes id / data-block and contenteditable that are associated with blocks
27000  * usage should be done on a cloned copy of the dom
27001  * @constructor
27002 * Run a new Attribute Filter { node : xxxx }}
27003 * @param {Object} config Configuration options
27004  */
27005 Roo.htmleditor.FilterBlock = function(cfg)
27006 {
27007     Roo.apply(this, cfg);
27008     var qa = cfg.node.querySelectorAll;
27009     this.removeAttributes('data-block');
27010     this.removeAttributes('contenteditable');
27011     this.removeAttributes('id');
27012     
27013 }
27014
27015 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27016 {
27017     node: true, // all tags
27018      
27019      
27020     removeAttributes : function(attr)
27021     {
27022         var ar = this.node.querySelectorAll('*[' + attr + ']');
27023         for (var i =0;i<ar.length;i++) {
27024             ar[i].removeAttribute(attr);
27025         }
27026     }
27027         
27028         
27029         
27030     
27031 });
27032 /**
27033  * @class Roo.htmleditor.KeyEnter
27034  * Handle Enter press..
27035  * @cfg {Roo.HtmlEditorCore} core the editor.
27036  * @constructor
27037  * Create a new Filter.
27038  * @param {Object} config Configuration options
27039  */
27040
27041
27042
27043
27044
27045 Roo.htmleditor.KeyEnter = function(cfg) {
27046     Roo.apply(this, cfg);
27047     // this does not actually call walk as it's really just a abstract class
27048  
27049     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
27050 }
27051
27052 //Roo.htmleditor.KeyEnter.i = 0;
27053
27054
27055 Roo.htmleditor.KeyEnter.prototype = {
27056     
27057     core : false,
27058     
27059     keypress : function(e)
27060     {
27061         if (e.charCode != 13 && e.charCode != 10) {
27062             Roo.log([e.charCode,e]);
27063             return true;
27064         }
27065         e.preventDefault();
27066         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
27067         var doc = this.core.doc;
27068           //add a new line
27069        
27070     
27071         var sel = this.core.getSelection();
27072         var range = sel.getRangeAt(0);
27073         var n = range.commonAncestorContainer;
27074         var pc = range.closest([ 'ol', 'ul']);
27075         var pli = range.closest('li');
27076         if (!pc || e.ctrlKey) {
27077             // on it list, or ctrl pressed.
27078             if (!e.ctrlKey) {
27079                 sel.insertNode('br', 'after'); 
27080             } else {
27081                 // only do this if we have ctrl key..
27082                 var br = doc.createElement('br');
27083                 br.className = 'clear';
27084                 br.setAttribute('style', 'clear: both');
27085                 sel.insertNode(br, 'after'); 
27086             }
27087             
27088          
27089             this.core.undoManager.addEvent();
27090             this.core.fireEditorEvent(e);
27091             return false;
27092         }
27093         
27094         // deal with <li> insetion
27095         if (pli.innerText.trim() == '' &&
27096             pli.previousSibling &&
27097             pli.previousSibling.nodeName == 'LI' &&
27098             pli.previousSibling.innerText.trim() ==  '') {
27099             pli.parentNode.removeChild(pli.previousSibling);
27100             sel.cursorAfter(pc);
27101             this.core.undoManager.addEvent();
27102             this.core.fireEditorEvent(e);
27103             return false;
27104         }
27105     
27106         var li = doc.createElement('LI');
27107         li.innerHTML = '&nbsp;';
27108         if (!pli || !pli.firstSibling) {
27109             pc.appendChild(li);
27110         } else {
27111             pli.parentNode.insertBefore(li, pli.firstSibling);
27112         }
27113         sel.cursorText (li.firstChild);
27114       
27115         this.core.undoManager.addEvent();
27116         this.core.fireEditorEvent(e);
27117
27118         return false;
27119         
27120     
27121         
27122         
27123          
27124     }
27125 };
27126      
27127 /**
27128  * @class Roo.htmleditor.Block
27129  * Base class for html editor blocks - do not use it directly .. extend it..
27130  * @cfg {DomElement} node The node to apply stuff to.
27131  * @cfg {String} friendly_name the name that appears in the context bar about this block
27132  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
27133  
27134  * @constructor
27135  * Create a new Filter.
27136  * @param {Object} config Configuration options
27137  */
27138
27139 Roo.htmleditor.Block  = function(cfg)
27140 {
27141     // do nothing .. should not be called really.
27142 }
27143 /**
27144  * factory method to get the block from an element (using cache if necessary)
27145  * @static
27146  * @param {HtmlElement} the dom element
27147  */
27148 Roo.htmleditor.Block.factory = function(node)
27149 {
27150     var cc = Roo.htmleditor.Block.cache;
27151     var id = Roo.get(node).id;
27152     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
27153         Roo.htmleditor.Block.cache[id].readElement(node);
27154         return Roo.htmleditor.Block.cache[id];
27155     }
27156     var db  = node.getAttribute('data-block');
27157     if (!db) {
27158         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27159     }
27160     var cls = Roo.htmleditor['Block' + db];
27161     if (typeof(cls) == 'undefined') {
27162         //Roo.log(node.getAttribute('data-block'));
27163         Roo.log("OOps missing block : " + 'Block' + db);
27164         return false;
27165     }
27166     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27167     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27168 };
27169
27170 /**
27171  * initalize all Elements from content that are 'blockable'
27172  * @static
27173  * @param the body element
27174  */
27175 Roo.htmleditor.Block.initAll = function(body, type)
27176 {
27177     if (typeof(type) == 'undefined') {
27178         var ia = Roo.htmleditor.Block.initAll;
27179         ia(body,'table');
27180         ia(body,'td');
27181         ia(body,'figure');
27182         return;
27183     }
27184     Roo.each(Roo.get(body).query(type), function(e) {
27185         Roo.htmleditor.Block.factory(e);    
27186     },this);
27187 };
27188 // question goes here... do we need to clear out this cache sometimes?
27189 // or show we make it relivant to the htmleditor.
27190 Roo.htmleditor.Block.cache = {};
27191
27192 Roo.htmleditor.Block.prototype = {
27193     
27194     node : false,
27195     
27196      // used by context menu
27197     friendly_name : 'Based Block',
27198     
27199     // text for button to delete this element
27200     deleteTitle : false,
27201     
27202     context : false,
27203     /**
27204      * Update a node with values from this object
27205      * @param {DomElement} node
27206      */
27207     updateElement : function(node)
27208     {
27209         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27210     },
27211      /**
27212      * convert to plain HTML for calling insertAtCursor..
27213      */
27214     toHTML : function()
27215     {
27216         return Roo.DomHelper.markup(this.toObject());
27217     },
27218     /**
27219      * used by readEleemnt to extract data from a node
27220      * may need improving as it's pretty basic
27221      
27222      * @param {DomElement} node
27223      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27224      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27225      * @param {String} style the style property - eg. text-align
27226      */
27227     getVal : function(node, tag, attr, style)
27228     {
27229         var n = node;
27230         if (tag !== true && n.tagName != tag.toUpperCase()) {
27231             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27232             // but kiss for now.
27233             n = node.getElementsByTagName(tag).item(0);
27234         }
27235         if (!n) {
27236             return '';
27237         }
27238         if (attr === false) {
27239             return n;
27240         }
27241         if (attr == 'html') {
27242             return n.innerHTML;
27243         }
27244         if (attr == 'style') {
27245             return n.style[style]; 
27246         }
27247         
27248         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27249             
27250     },
27251     /**
27252      * create a DomHelper friendly object - for use with 
27253      * Roo.DomHelper.markup / overwrite / etc..
27254      * (override this)
27255      */
27256     toObject : function()
27257     {
27258         return {};
27259     },
27260       /**
27261      * Read a node that has a 'data-block' property - and extract the values from it.
27262      * @param {DomElement} node - the node
27263      */
27264     readElement : function(node)
27265     {
27266         
27267     } 
27268     
27269     
27270 };
27271
27272  
27273
27274 /**
27275  * @class Roo.htmleditor.BlockFigure
27276  * Block that has an image and a figcaption
27277  * @cfg {String} image_src the url for the image
27278  * @cfg {String} align (left|right) alignment for the block default left
27279  * @cfg {String} caption the text to appear below  (and in the alt tag)
27280  * @cfg {String} caption_display (block|none) display or not the caption
27281  * @cfg {String|number} image_width the width of the image number or %?
27282  * @cfg {String|number} image_height the height of the image number or %?
27283  * 
27284  * @constructor
27285  * Create a new Filter.
27286  * @param {Object} config Configuration options
27287  */
27288
27289 Roo.htmleditor.BlockFigure = function(cfg)
27290 {
27291     if (cfg.node) {
27292         this.readElement(cfg.node);
27293         this.updateElement(cfg.node);
27294     }
27295     Roo.apply(this, cfg);
27296 }
27297 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27298  
27299     
27300     // setable values.
27301     image_src: '',
27302     align: 'center',
27303     caption : '',
27304     caption_display : 'block',
27305     width : '100%',
27306     cls : '',
27307     href: '',
27308     video_url : '',
27309     
27310     // margin: '2%', not used
27311     
27312     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27313
27314     
27315     // used by context menu
27316     friendly_name : 'Image with caption',
27317     deleteTitle : "Delete Image and Caption",
27318     
27319     contextMenu : function(toolbar)
27320     {
27321         
27322         var block = function() {
27323             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27324         };
27325         
27326         
27327         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27328         
27329         var syncValue = toolbar.editorcore.syncValue;
27330         
27331         var fields = {};
27332         
27333         return [
27334              {
27335                 xtype : 'TextItem',
27336                 text : "Source: ",
27337                 xns : rooui.Toolbar  //Boostrap?
27338             },
27339             {
27340                 xtype : 'Button',
27341                 text: 'Change Image URL',
27342                  
27343                 listeners : {
27344                     click: function (btn, state)
27345                     {
27346                         var b = block();
27347                         
27348                         Roo.MessageBox.show({
27349                             title : "Image Source URL",
27350                             msg : "Enter the url for the image",
27351                             buttons: Roo.MessageBox.OKCANCEL,
27352                             fn: function(btn, val){
27353                                 if (btn != 'ok') {
27354                                     return;
27355                                 }
27356                                 b.image_src = val;
27357                                 b.updateElement();
27358                                 syncValue();
27359                                 toolbar.editorcore.onEditorEvent();
27360                             },
27361                             minWidth:250,
27362                             prompt:true,
27363                             //multiline: multiline,
27364                             modal : true,
27365                             value : b.image_src
27366                         });
27367                     }
27368                 },
27369                 xns : rooui.Toolbar
27370             },
27371          
27372             {
27373                 xtype : 'Button',
27374                 text: 'Change Link URL',
27375                  
27376                 listeners : {
27377                     click: function (btn, state)
27378                     {
27379                         var b = block();
27380                         
27381                         Roo.MessageBox.show({
27382                             title : "Link URL",
27383                             msg : "Enter the url for the link - leave blank to have no link",
27384                             buttons: Roo.MessageBox.OKCANCEL,
27385                             fn: function(btn, val){
27386                                 if (btn != 'ok') {
27387                                     return;
27388                                 }
27389                                 b.href = val;
27390                                 b.updateElement();
27391                                 syncValue();
27392                                 toolbar.editorcore.onEditorEvent();
27393                             },
27394                             minWidth:250,
27395                             prompt:true,
27396                             //multiline: multiline,
27397                             modal : true,
27398                             value : b.href
27399                         });
27400                     }
27401                 },
27402                 xns : rooui.Toolbar
27403             },
27404             {
27405                 xtype : 'Button',
27406                 text: 'Show Video URL',
27407                  
27408                 listeners : {
27409                     click: function (btn, state)
27410                     {
27411                         Roo.MessageBox.alert("Video URL",
27412                             block().video_url == '' ? 'This image is not linked ot a video' :
27413                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27414                     }
27415                 },
27416                 xns : rooui.Toolbar
27417             },
27418             
27419             
27420             {
27421                 xtype : 'TextItem',
27422                 text : "Width: ",
27423                 xns : rooui.Toolbar  //Boostrap?
27424             },
27425             {
27426                 xtype : 'ComboBox',
27427                 allowBlank : false,
27428                 displayField : 'val',
27429                 editable : true,
27430                 listWidth : 100,
27431                 triggerAction : 'all',
27432                 typeAhead : true,
27433                 valueField : 'val',
27434                 width : 70,
27435                 name : 'width',
27436                 listeners : {
27437                     select : function (combo, r, index)
27438                     {
27439                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27440                         var b = block();
27441                         b.width = r.get('val');
27442                         b.updateElement();
27443                         syncValue();
27444                         toolbar.editorcore.onEditorEvent();
27445                     }
27446                 },
27447                 xns : rooui.form,
27448                 store : {
27449                     xtype : 'SimpleStore',
27450                     data : [
27451                         ['100%'],
27452                         ['80%'],
27453                         ['50%'],
27454                         ['20%'],
27455                         ['10%']
27456                     ],
27457                     fields : [ 'val'],
27458                     xns : Roo.data
27459                 }
27460             },
27461             {
27462                 xtype : 'TextItem',
27463                 text : "Align: ",
27464                 xns : rooui.Toolbar  //Boostrap?
27465             },
27466             {
27467                 xtype : 'ComboBox',
27468                 allowBlank : false,
27469                 displayField : 'val',
27470                 editable : true,
27471                 listWidth : 100,
27472                 triggerAction : 'all',
27473                 typeAhead : true,
27474                 valueField : 'val',
27475                 width : 70,
27476                 name : 'align',
27477                 listeners : {
27478                     select : function (combo, r, index)
27479                     {
27480                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27481                         var b = block();
27482                         b.align = r.get('val');
27483                         b.updateElement();
27484                         syncValue();
27485                         toolbar.editorcore.onEditorEvent();
27486                     }
27487                 },
27488                 xns : rooui.form,
27489                 store : {
27490                     xtype : 'SimpleStore',
27491                     data : [
27492                         ['left'],
27493                         ['right'],
27494                         ['center']
27495                     ],
27496                     fields : [ 'val'],
27497                     xns : Roo.data
27498                 }
27499             },
27500             
27501             
27502             {
27503                 xtype : 'Button',
27504                 text: 'Hide Caption',
27505                 name : 'caption_display',
27506                 pressed : false,
27507                 enableToggle : true,
27508                 setValue : function(v) {
27509                     // this trigger toggle.
27510                      
27511                     this.setText(v ? "Hide Caption" : "Show Caption");
27512                     this.setPressed(v != 'block');
27513                 },
27514                 listeners : {
27515                     toggle: function (btn, state)
27516                     {
27517                         var b  = block();
27518                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27519                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27520                         b.updateElement();
27521                         syncValue();
27522                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27523                         toolbar.editorcore.onEditorEvent();
27524                     }
27525                 },
27526                 xns : rooui.Toolbar
27527             }
27528         ];
27529         
27530     },
27531     /**
27532      * create a DomHelper friendly object - for use with
27533      * Roo.DomHelper.markup / overwrite / etc..
27534      */
27535     toObject : function()
27536     {
27537         var d = document.createElement('div');
27538         d.innerHTML = this.caption;
27539         
27540         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27541         
27542         var iw = this.align == 'center' ? this.width : '100%';
27543         var img =   {
27544             tag : 'img',
27545             contenteditable : 'false',
27546             src : this.image_src,
27547             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27548             style: {
27549                 width : iw,
27550                 maxWidth : iw + ' !important', // this is not getting rendered?
27551                 margin : m  
27552                 
27553             }
27554         };
27555         /*
27556         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27557                     '<a href="{2}">' + 
27558                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27559                     '</a>' + 
27560                 '</div>',
27561         */
27562                 
27563         if (this.href.length > 0) {
27564             img = {
27565                 tag : 'a',
27566                 href: this.href,
27567                 contenteditable : 'true',
27568                 cn : [
27569                     img
27570                 ]
27571             };
27572         }
27573         
27574         
27575         if (this.video_url.length > 0) {
27576             img = {
27577                 tag : 'div',
27578                 cls : this.cls,
27579                 frameborder : 0,
27580                 allowfullscreen : true,
27581                 width : 420,  // these are for video tricks - that we replace the outer
27582                 height : 315,
27583                 src : this.video_url,
27584                 cn : [
27585                     img
27586                 ]
27587             };
27588         }
27589         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27590         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27591         
27592   
27593         var ret =   {
27594             tag: 'figure',
27595             'data-block' : 'Figure',
27596             'data-width' : this.width, 
27597             contenteditable : 'false',
27598             
27599             style : {
27600                 display: 'block',
27601                 float :  this.align ,
27602                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27603                 width : this.align == 'center' ? '100%' : this.width,
27604                 margin:  '0px',
27605                 padding: this.align == 'center' ? '0' : '0 10px' ,
27606                 textAlign : this.align   // seems to work for email..
27607                 
27608             },
27609            
27610             
27611             align : this.align,
27612             cn : [
27613                 img,
27614               
27615                 {
27616                     tag: 'figcaption',
27617                     'data-display' : this.caption_display,
27618                     style : {
27619                         textAlign : 'left',
27620                         fontSize : '16px',
27621                         lineHeight : '24px',
27622                         display : this.caption_display,
27623                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27624                         margin: m,
27625                         width: this.align == 'center' ?  this.width : '100%' 
27626                     
27627                          
27628                     },
27629                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27630                     cn : [
27631                         {
27632                             tag: 'div',
27633                             style  : {
27634                                 marginTop : '16px',
27635                                 textAlign : 'left'
27636                             },
27637                             align: 'left',
27638                             cn : [
27639                                 {
27640                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27641                                     tag : 'i',
27642                                     contenteditable : true,
27643                                     html : captionhtml
27644                                 }
27645                                 
27646                             ]
27647                         }
27648                         
27649                     ]
27650                     
27651                 }
27652             ]
27653         };
27654         return ret;
27655          
27656     },
27657     
27658     readElement : function(node)
27659     {
27660         // this should not really come from the link...
27661         this.video_url = this.getVal(node, 'div', 'src');
27662         this.cls = this.getVal(node, 'div', 'class');
27663         this.href = this.getVal(node, 'a', 'href');
27664         
27665         
27666         this.image_src = this.getVal(node, 'img', 'src');
27667          
27668         this.align = this.getVal(node, 'figure', 'align');
27669         var figcaption = this.getVal(node, 'figcaption', false);
27670         if (figcaption !== '') {
27671             this.caption = this.getVal(figcaption, 'i', 'html');
27672         }
27673         
27674
27675         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27676         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27677         this.width = this.getVal(node, true, 'data-width');
27678         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27679         
27680     },
27681     removeNode : function()
27682     {
27683         return this.node;
27684     }
27685     
27686   
27687    
27688      
27689     
27690     
27691     
27692     
27693 })
27694
27695  
27696
27697 /**
27698  * @class Roo.htmleditor.BlockTable
27699  * Block that manages a table
27700  * 
27701  * @constructor
27702  * Create a new Filter.
27703  * @param {Object} config Configuration options
27704  */
27705
27706 Roo.htmleditor.BlockTable = function(cfg)
27707 {
27708     if (cfg.node) {
27709         this.readElement(cfg.node);
27710         this.updateElement(cfg.node);
27711     }
27712     Roo.apply(this, cfg);
27713     if (!cfg.node) {
27714         this.rows = [];
27715         for(var r = 0; r < this.no_row; r++) {
27716             this.rows[r] = [];
27717             for(var c = 0; c < this.no_col; c++) {
27718                 this.rows[r][c] = this.emptyCell();
27719             }
27720         }
27721     }
27722     
27723     
27724 }
27725 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27726  
27727     rows : false,
27728     no_col : 1,
27729     no_row : 1,
27730     
27731     
27732     width: '100%',
27733     
27734     // used by context menu
27735     friendly_name : 'Table',
27736     deleteTitle : 'Delete Table',
27737     // context menu is drawn once..
27738     
27739     contextMenu : function(toolbar)
27740     {
27741         
27742         var block = function() {
27743             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27744         };
27745         
27746         
27747         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27748         
27749         var syncValue = toolbar.editorcore.syncValue;
27750         
27751         var fields = {};
27752         
27753         return [
27754             {
27755                 xtype : 'TextItem',
27756                 text : "Width: ",
27757                 xns : rooui.Toolbar  //Boostrap?
27758             },
27759             {
27760                 xtype : 'ComboBox',
27761                 allowBlank : false,
27762                 displayField : 'val',
27763                 editable : true,
27764                 listWidth : 100,
27765                 triggerAction : 'all',
27766                 typeAhead : true,
27767                 valueField : 'val',
27768                 width : 100,
27769                 name : 'width',
27770                 listeners : {
27771                     select : function (combo, r, index)
27772                     {
27773                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27774                         var b = block();
27775                         b.width = r.get('val');
27776                         b.updateElement();
27777                         syncValue();
27778                         toolbar.editorcore.onEditorEvent();
27779                     }
27780                 },
27781                 xns : rooui.form,
27782                 store : {
27783                     xtype : 'SimpleStore',
27784                     data : [
27785                         ['100%'],
27786                         ['auto']
27787                     ],
27788                     fields : [ 'val'],
27789                     xns : Roo.data
27790                 }
27791             },
27792             // -------- Cols
27793             
27794             {
27795                 xtype : 'TextItem',
27796                 text : "Columns: ",
27797                 xns : rooui.Toolbar  //Boostrap?
27798             },
27799          
27800             {
27801                 xtype : 'Button',
27802                 text: '-',
27803                 listeners : {
27804                     click : function (_self, e)
27805                     {
27806                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27807                         block().removeColumn();
27808                         syncValue();
27809                         toolbar.editorcore.onEditorEvent();
27810                     }
27811                 },
27812                 xns : rooui.Toolbar
27813             },
27814             {
27815                 xtype : 'Button',
27816                 text: '+',
27817                 listeners : {
27818                     click : function (_self, e)
27819                     {
27820                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27821                         block().addColumn();
27822                         syncValue();
27823                         toolbar.editorcore.onEditorEvent();
27824                     }
27825                 },
27826                 xns : rooui.Toolbar
27827             },
27828             // -------- ROWS
27829             {
27830                 xtype : 'TextItem',
27831                 text : "Rows: ",
27832                 xns : rooui.Toolbar  //Boostrap?
27833             },
27834          
27835             {
27836                 xtype : 'Button',
27837                 text: '-',
27838                 listeners : {
27839                     click : function (_self, e)
27840                     {
27841                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27842                         block().removeRow();
27843                         syncValue();
27844                         toolbar.editorcore.onEditorEvent();
27845                     }
27846                 },
27847                 xns : rooui.Toolbar
27848             },
27849             {
27850                 xtype : 'Button',
27851                 text: '+',
27852                 listeners : {
27853                     click : function (_self, e)
27854                     {
27855                         block().addRow();
27856                         syncValue();
27857                         toolbar.editorcore.onEditorEvent();
27858                     }
27859                 },
27860                 xns : rooui.Toolbar
27861             },
27862             // -------- ROWS
27863             {
27864                 xtype : 'Button',
27865                 text: 'Reset Column Widths',
27866                 listeners : {
27867                     
27868                     click : function (_self, e)
27869                     {
27870                         block().resetWidths();
27871                         syncValue();
27872                         toolbar.editorcore.onEditorEvent();
27873                     }
27874                 },
27875                 xns : rooui.Toolbar
27876             } 
27877             
27878             
27879             
27880         ];
27881         
27882     },
27883     
27884     
27885   /**
27886      * create a DomHelper friendly object - for use with
27887      * Roo.DomHelper.markup / overwrite / etc..
27888      * ?? should it be called with option to hide all editing features?
27889      */
27890     toObject : function()
27891     {
27892         
27893         var ret = {
27894             tag : 'table',
27895             contenteditable : 'false', // this stops cell selection from picking the table.
27896             'data-block' : 'Table',
27897             style : {
27898                 width:  this.width,
27899                 border : 'solid 1px #000', // ??? hard coded?
27900                 'border-collapse' : 'collapse' 
27901             },
27902             cn : [
27903                 { tag : 'tbody' , cn : [] }
27904             ]
27905         };
27906         
27907         // do we have a head = not really 
27908         var ncols = 0;
27909         Roo.each(this.rows, function( row ) {
27910             var tr = {
27911                 tag: 'tr',
27912                 style : {
27913                     margin: '6px',
27914                     border : 'solid 1px #000',
27915                     textAlign : 'left' 
27916                 },
27917                 cn : [ ]
27918             };
27919             
27920             ret.cn[0].cn.push(tr);
27921             // does the row have any properties? ?? height?
27922             var nc = 0;
27923             Roo.each(row, function( cell ) {
27924                 
27925                 var td = {
27926                     tag : 'td',
27927                     contenteditable :  'true',
27928                     'data-block' : 'Td',
27929                     html : cell.html,
27930                     style : cell.style
27931                 };
27932                 if (cell.colspan > 1) {
27933                     td.colspan = cell.colspan ;
27934                     nc += cell.colspan;
27935                 } else {
27936                     nc++;
27937                 }
27938                 if (cell.rowspan > 1) {
27939                     td.rowspan = cell.rowspan ;
27940                 }
27941                 
27942                 
27943                 // widths ?
27944                 tr.cn.push(td);
27945                     
27946                 
27947             }, this);
27948             ncols = Math.max(nc, ncols);
27949             
27950             
27951         }, this);
27952         // add the header row..
27953         
27954         ncols++;
27955          
27956         
27957         return ret;
27958          
27959     },
27960     
27961     readElement : function(node)
27962     {
27963         node  = node ? node : this.node ;
27964         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27965         
27966         this.rows = [];
27967         this.no_row = 0;
27968         var trs = Array.from(node.rows);
27969         trs.forEach(function(tr) {
27970             var row =  [];
27971             this.rows.push(row);
27972             
27973             this.no_row++;
27974             var no_column = 0;
27975             Array.from(tr.cells).forEach(function(td) {
27976                 
27977                 var add = {
27978                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27979                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27980                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27981                     html : td.innerHTML
27982                 };
27983                 no_column += add.colspan;
27984                      
27985                 
27986                 row.push(add);
27987                 
27988                 
27989             },this);
27990             this.no_col = Math.max(this.no_col, no_column);
27991             
27992             
27993         },this);
27994         
27995         
27996     },
27997     normalizeRows: function()
27998     {
27999         var ret= [];
28000         var rid = -1;
28001         this.rows.forEach(function(row) {
28002             rid++;
28003             ret[rid] = [];
28004             row = this.normalizeRow(row);
28005             var cid = 0;
28006             row.forEach(function(c) {
28007                 while (typeof(ret[rid][cid]) != 'undefined') {
28008                     cid++;
28009                 }
28010                 if (typeof(ret[rid]) == 'undefined') {
28011                     ret[rid] = [];
28012                 }
28013                 ret[rid][cid] = c;
28014                 c.row = rid;
28015                 c.col = cid;
28016                 if (c.rowspan < 2) {
28017                     return;
28018                 }
28019                 
28020                 for(var i = 1 ;i < c.rowspan; i++) {
28021                     if (typeof(ret[rid+i]) == 'undefined') {
28022                         ret[rid+i] = [];
28023                     }
28024                     ret[rid+i][cid] = c;
28025                 }
28026             });
28027         }, this);
28028         return ret;
28029     
28030     },
28031     
28032     normalizeRow: function(row)
28033     {
28034         var ret= [];
28035         row.forEach(function(c) {
28036             if (c.colspan < 2) {
28037                 ret.push(c);
28038                 return;
28039             }
28040             for(var i =0 ;i < c.colspan; i++) {
28041                 ret.push(c);
28042             }
28043         });
28044         return ret;
28045     
28046     },
28047     
28048     deleteColumn : function(sel)
28049     {
28050         if (!sel || sel.type != 'col') {
28051             return;
28052         }
28053         if (this.no_col < 2) {
28054             return;
28055         }
28056         
28057         this.rows.forEach(function(row) {
28058             var cols = this.normalizeRow(row);
28059             var col = cols[sel.col];
28060             if (col.colspan > 1) {
28061                 col.colspan --;
28062             } else {
28063                 row.remove(col);
28064             }
28065             
28066         }, this);
28067         this.no_col--;
28068         
28069     },
28070     removeColumn : function()
28071     {
28072         this.deleteColumn({
28073             type: 'col',
28074             col : this.no_col-1
28075         });
28076         this.updateElement();
28077     },
28078     
28079      
28080     addColumn : function()
28081     {
28082         
28083         this.rows.forEach(function(row) {
28084             row.push(this.emptyCell());
28085            
28086         }, this);
28087         this.updateElement();
28088     },
28089     
28090     deleteRow : function(sel)
28091     {
28092         if (!sel || sel.type != 'row') {
28093             return;
28094         }
28095         
28096         if (this.no_row < 2) {
28097             return;
28098         }
28099         
28100         var rows = this.normalizeRows();
28101         
28102         
28103         rows[sel.row].forEach(function(col) {
28104             if (col.rowspan > 1) {
28105                 col.rowspan--;
28106             } else {
28107                 col.remove = 1; // flage it as removed.
28108             }
28109             
28110         }, this);
28111         var newrows = [];
28112         this.rows.forEach(function(row) {
28113             newrow = [];
28114             row.forEach(function(c) {
28115                 if (typeof(c.remove) == 'undefined') {
28116                     newrow.push(c);
28117                 }
28118                 
28119             });
28120             if (newrow.length > 0) {
28121                 newrows.push(row);
28122             }
28123         });
28124         this.rows =  newrows;
28125         
28126         
28127         
28128         this.no_row--;
28129         this.updateElement();
28130         
28131     },
28132     removeRow : function()
28133     {
28134         this.deleteRow({
28135             type: 'row',
28136             row : this.no_row-1
28137         });
28138         
28139     },
28140     
28141      
28142     addRow : function()
28143     {
28144         
28145         var row = [];
28146         for (var i = 0; i < this.no_col; i++ ) {
28147             
28148             row.push(this.emptyCell());
28149            
28150         }
28151         this.rows.push(row);
28152         this.updateElement();
28153         
28154     },
28155      
28156     // the default cell object... at present...
28157     emptyCell : function() {
28158         return (new Roo.htmleditor.BlockTd({})).toObject();
28159         
28160      
28161     },
28162     
28163     removeNode : function()
28164     {
28165         return this.node;
28166     },
28167     
28168     
28169     
28170     resetWidths : function()
28171     {
28172         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28173             var nn = Roo.htmleditor.Block.factory(n);
28174             nn.width = '';
28175             nn.updateElement(n);
28176         });
28177     }
28178     
28179     
28180     
28181     
28182 })
28183
28184 /**
28185  *
28186  * editing a TD?
28187  *
28188  * since selections really work on the table cell, then editing really should work from there
28189  *
28190  * The original plan was to support merging etc... - but that may not be needed yet..
28191  *
28192  * So this simple version will support:
28193  *   add/remove cols
28194  *   adjust the width +/-
28195  *   reset the width...
28196  *   
28197  *
28198  */
28199
28200
28201  
28202
28203 /**
28204  * @class Roo.htmleditor.BlockTable
28205  * Block that manages a table
28206  * 
28207  * @constructor
28208  * Create a new Filter.
28209  * @param {Object} config Configuration options
28210  */
28211
28212 Roo.htmleditor.BlockTd = function(cfg)
28213 {
28214     if (cfg.node) {
28215         this.readElement(cfg.node);
28216         this.updateElement(cfg.node);
28217     }
28218     Roo.apply(this, cfg);
28219      
28220     
28221     
28222 }
28223 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28224  
28225     node : false,
28226     
28227     width: '',
28228     textAlign : 'left',
28229     valign : 'top',
28230     
28231     colspan : 1,
28232     rowspan : 1,
28233     
28234     
28235     // used by context menu
28236     friendly_name : 'Table Cell',
28237     deleteTitle : false, // use our customer delete
28238     
28239     // context menu is drawn once..
28240     
28241     contextMenu : function(toolbar)
28242     {
28243         
28244         var cell = function() {
28245             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28246         };
28247         
28248         var table = function() {
28249             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28250         };
28251         
28252         var lr = false;
28253         var saveSel = function()
28254         {
28255             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28256         }
28257         var restoreSel = function()
28258         {
28259             if (lr) {
28260                 (function() {
28261                     toolbar.editorcore.focus();
28262                     var cr = toolbar.editorcore.getSelection();
28263                     cr.removeAllRanges();
28264                     cr.addRange(lr);
28265                     toolbar.editorcore.onEditorEvent();
28266                 }).defer(10, this);
28267                 
28268                 
28269             }
28270         }
28271         
28272         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28273         
28274         var syncValue = toolbar.editorcore.syncValue;
28275         
28276         var fields = {};
28277         
28278         return [
28279             {
28280                 xtype : 'Button',
28281                 text : 'Edit Table',
28282                 listeners : {
28283                     click : function() {
28284                         var t = toolbar.tb.selectedNode.closest('table');
28285                         toolbar.editorcore.selectNode(t);
28286                         toolbar.editorcore.onEditorEvent();                        
28287                     }
28288                 }
28289                 
28290             },
28291               
28292            
28293              
28294             {
28295                 xtype : 'TextItem',
28296                 text : "Column Width: ",
28297                  xns : rooui.Toolbar 
28298                
28299             },
28300             {
28301                 xtype : 'Button',
28302                 text: '-',
28303                 listeners : {
28304                     click : function (_self, e)
28305                     {
28306                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28307                         cell().shrinkColumn();
28308                         syncValue();
28309                          toolbar.editorcore.onEditorEvent();
28310                     }
28311                 },
28312                 xns : rooui.Toolbar
28313             },
28314             {
28315                 xtype : 'Button',
28316                 text: '+',
28317                 listeners : {
28318                     click : function (_self, e)
28319                     {
28320                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28321                         cell().growColumn();
28322                         syncValue();
28323                         toolbar.editorcore.onEditorEvent();
28324                     }
28325                 },
28326                 xns : rooui.Toolbar
28327             },
28328             
28329             {
28330                 xtype : 'TextItem',
28331                 text : "Vertical Align: ",
28332                 xns : rooui.Toolbar  //Boostrap?
28333             },
28334             {
28335                 xtype : 'ComboBox',
28336                 allowBlank : false,
28337                 displayField : 'val',
28338                 editable : true,
28339                 listWidth : 100,
28340                 triggerAction : 'all',
28341                 typeAhead : true,
28342                 valueField : 'val',
28343                 width : 100,
28344                 name : 'valign',
28345                 listeners : {
28346                     select : function (combo, r, index)
28347                     {
28348                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28349                         var b = cell();
28350                         b.valign = r.get('val');
28351                         b.updateElement();
28352                         syncValue();
28353                         toolbar.editorcore.onEditorEvent();
28354                     }
28355                 },
28356                 xns : rooui.form,
28357                 store : {
28358                     xtype : 'SimpleStore',
28359                     data : [
28360                         ['top'],
28361                         ['middle'],
28362                         ['bottom'] // there are afew more... 
28363                     ],
28364                     fields : [ 'val'],
28365                     xns : Roo.data
28366                 }
28367             },
28368             
28369             {
28370                 xtype : 'TextItem',
28371                 text : "Merge Cells: ",
28372                  xns : rooui.Toolbar 
28373                
28374             },
28375             
28376             
28377             {
28378                 xtype : 'Button',
28379                 text: 'Right',
28380                 listeners : {
28381                     click : function (_self, e)
28382                     {
28383                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28384                         cell().mergeRight();
28385                         //block().growColumn();
28386                         syncValue();
28387                         toolbar.editorcore.onEditorEvent();
28388                     }
28389                 },
28390                 xns : rooui.Toolbar
28391             },
28392              
28393             {
28394                 xtype : 'Button',
28395                 text: 'Below',
28396                 listeners : {
28397                     click : function (_self, e)
28398                     {
28399                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28400                         cell().mergeBelow();
28401                         //block().growColumn();
28402                         syncValue();
28403                         toolbar.editorcore.onEditorEvent();
28404                     }
28405                 },
28406                 xns : rooui.Toolbar
28407             },
28408             {
28409                 xtype : 'TextItem',
28410                 text : "| ",
28411                  xns : rooui.Toolbar 
28412                
28413             },
28414             
28415             {
28416                 xtype : 'Button',
28417                 text: 'Split',
28418                 listeners : {
28419                     click : function (_self, e)
28420                     {
28421                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28422                         cell().split();
28423                         syncValue();
28424                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28425                         toolbar.editorcore.onEditorEvent();
28426                                              
28427                     }
28428                 },
28429                 xns : rooui.Toolbar
28430             },
28431             {
28432                 xtype : 'Fill',
28433                 xns : rooui.Toolbar 
28434                
28435             },
28436         
28437           
28438             {
28439                 xtype : 'Button',
28440                 text: 'Delete',
28441                  
28442                 xns : rooui.Toolbar,
28443                 menu : {
28444                     xtype : 'Menu',
28445                     xns : rooui.menu,
28446                     items : [
28447                         {
28448                             xtype : 'Item',
28449                             html: 'Column',
28450                             listeners : {
28451                                 click : function (_self, e)
28452                                 {
28453                                     var t = table();
28454                                     
28455                                     cell().deleteColumn();
28456                                     syncValue();
28457                                     toolbar.editorcore.selectNode(t.node);
28458                                     toolbar.editorcore.onEditorEvent();   
28459                                 }
28460                             },
28461                             xns : rooui.menu
28462                         },
28463                         {
28464                             xtype : 'Item',
28465                             html: 'Row',
28466                             listeners : {
28467                                 click : function (_self, e)
28468                                 {
28469                                     var t = table();
28470                                     cell().deleteRow();
28471                                     syncValue();
28472                                     
28473                                     toolbar.editorcore.selectNode(t.node);
28474                                     toolbar.editorcore.onEditorEvent();   
28475                                                          
28476                                 }
28477                             },
28478                             xns : rooui.menu
28479                         },
28480                        {
28481                             xtype : 'Separator',
28482                             xns : rooui.menu
28483                         },
28484                         {
28485                             xtype : 'Item',
28486                             html: 'Table',
28487                             listeners : {
28488                                 click : function (_self, e)
28489                                 {
28490                                     var t = table();
28491                                     var nn = t.node.nextSibling || t.node.previousSibling;
28492                                     t.node.parentNode.removeChild(t.node);
28493                                     if (nn) { 
28494                                         toolbar.editorcore.selectNode(nn, true);
28495                                     }
28496                                     toolbar.editorcore.onEditorEvent();   
28497                                                          
28498                                 }
28499                             },
28500                             xns : rooui.menu
28501                         }
28502                     ]
28503                 }
28504             }
28505             
28506             // align... << fixme
28507             
28508         ];
28509         
28510     },
28511     
28512     
28513   /**
28514      * create a DomHelper friendly object - for use with
28515      * Roo.DomHelper.markup / overwrite / etc..
28516      * ?? should it be called with option to hide all editing features?
28517      */
28518  /**
28519      * create a DomHelper friendly object - for use with
28520      * Roo.DomHelper.markup / overwrite / etc..
28521      * ?? should it be called with option to hide all editing features?
28522      */
28523     toObject : function()
28524     {
28525         var ret = {
28526             tag : 'td',
28527             contenteditable : 'true', // this stops cell selection from picking the table.
28528             'data-block' : 'Td',
28529             valign : this.valign,
28530             style : {  
28531                 'text-align' :  this.textAlign,
28532                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28533                 'border-collapse' : 'collapse',
28534                 padding : '6px', // 8 for desktop / 4 for mobile
28535                 'vertical-align': this.valign
28536             },
28537             html : this.html
28538         };
28539         if (this.width != '') {
28540             ret.width = this.width;
28541             ret.style.width = this.width;
28542         }
28543         
28544         
28545         if (this.colspan > 1) {
28546             ret.colspan = this.colspan ;
28547         } 
28548         if (this.rowspan > 1) {
28549             ret.rowspan = this.rowspan ;
28550         }
28551         
28552            
28553         
28554         return ret;
28555          
28556     },
28557     
28558     readElement : function(node)
28559     {
28560         node  = node ? node : this.node ;
28561         this.width = node.style.width;
28562         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28563         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28564         this.html = node.innerHTML;
28565         if (node.style.textAlign != '') {
28566             this.textAlign = node.style.textAlign;
28567         }
28568         
28569         
28570     },
28571      
28572     // the default cell object... at present...
28573     emptyCell : function() {
28574         return {
28575             colspan :  1,
28576             rowspan :  1,
28577             textAlign : 'left',
28578             html : "&nbsp;" // is this going to be editable now?
28579         };
28580      
28581     },
28582     
28583     removeNode : function()
28584     {
28585         return this.node.closest('table');
28586          
28587     },
28588     
28589     cellData : false,
28590     
28591     colWidths : false,
28592     
28593     toTableArray  : function()
28594     {
28595         var ret = [];
28596         var tab = this.node.closest('tr').closest('table');
28597         Array.from(tab.rows).forEach(function(r, ri){
28598             ret[ri] = [];
28599         });
28600         var rn = 0;
28601         this.colWidths = [];
28602         var all_auto = true;
28603         Array.from(tab.rows).forEach(function(r, ri){
28604             
28605             var cn = 0;
28606             Array.from(r.cells).forEach(function(ce, ci){
28607                 var c =  {
28608                     cell : ce,
28609                     row : rn,
28610                     col: cn,
28611                     colspan : ce.colSpan,
28612                     rowspan : ce.rowSpan
28613                 };
28614                 if (ce.isEqualNode(this.node)) {
28615                     this.cellData = c;
28616                 }
28617                 // if we have been filled up by a row?
28618                 if (typeof(ret[rn][cn]) != 'undefined') {
28619                     while(typeof(ret[rn][cn]) != 'undefined') {
28620                         cn++;
28621                     }
28622                     c.col = cn;
28623                 }
28624                 
28625                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
28626                     this.colWidths[cn] =   ce.style.width;
28627                     if (this.colWidths[cn] != '') {
28628                         all_auto = false;
28629                     }
28630                 }
28631                 
28632                 
28633                 if (c.colspan < 2 && c.rowspan < 2 ) {
28634                     ret[rn][cn] = c;
28635                     cn++;
28636                     return;
28637                 }
28638                 for(var j = 0; j < c.rowspan; j++) {
28639                     if (typeof(ret[rn+j]) == 'undefined') {
28640                         continue; // we have a problem..
28641                     }
28642                     ret[rn+j][cn] = c;
28643                     for(var i = 0; i < c.colspan; i++) {
28644                         ret[rn+j][cn+i] = c;
28645                     }
28646                 }
28647                 
28648                 cn += c.colspan;
28649             }, this);
28650             rn++;
28651         }, this);
28652         
28653         // initalize widths.?
28654         // either all widths or no widths..
28655         if (all_auto) {
28656             this.colWidths[0] = false; // no widths flag.
28657         }
28658         
28659         
28660         return ret;
28661         
28662     },
28663     
28664     
28665     
28666     
28667     mergeRight: function()
28668     {
28669          
28670         // get the contents of the next cell along..
28671         var tr = this.node.closest('tr');
28672         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28673         if (i >= tr.childNodes.length - 1) {
28674             return; // no cells on right to merge with.
28675         }
28676         var table = this.toTableArray();
28677         
28678         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28679             return; // nothing right?
28680         }
28681         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28682         // right cell - must be same rowspan and on the same row.
28683         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28684             return; // right hand side is not same rowspan.
28685         }
28686         
28687         
28688         
28689         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28690         tr.removeChild(rc.cell);
28691         this.colspan += rc.colspan;
28692         this.node.setAttribute('colspan', this.colspan);
28693
28694         var table = this.toTableArray();
28695         this.normalizeWidths(table);
28696         this.updateWidths(table);
28697     },
28698     
28699     
28700     mergeBelow : function()
28701     {
28702         var table = this.toTableArray();
28703         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28704             return; // no row below
28705         }
28706         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28707             return; // nothing right?
28708         }
28709         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28710         
28711         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28712             return; // right hand side is not same rowspan.
28713         }
28714         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28715         rc.cell.parentNode.removeChild(rc.cell);
28716         this.rowspan += rc.rowspan;
28717         this.node.setAttribute('rowspan', this.rowspan);
28718     },
28719     
28720     split: function()
28721     {
28722         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28723             return;
28724         }
28725         var table = this.toTableArray();
28726         var cd = this.cellData;
28727         this.rowspan = 1;
28728         this.colspan = 1;
28729         
28730         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28731              
28732             
28733             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28734                 if (r == cd.row && c == cd.col) {
28735                     this.node.removeAttribute('rowspan');
28736                     this.node.removeAttribute('colspan');
28737                 }
28738                  
28739                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28740                 ntd.removeAttribute('id'); 
28741                 ntd.style.width  = this.colWidths[c];
28742                 ntd.innerHTML = '';
28743                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28744             }
28745             
28746         }
28747         this.redrawAllCells(table);
28748         
28749     },
28750     
28751     
28752     
28753     redrawAllCells: function(table)
28754     {
28755         
28756          
28757         var tab = this.node.closest('tr').closest('table');
28758         var ctr = tab.rows[0].parentNode;
28759         Array.from(tab.rows).forEach(function(r, ri){
28760             
28761             Array.from(r.cells).forEach(function(ce, ci){
28762                 ce.parentNode.removeChild(ce);
28763             });
28764             r.parentNode.removeChild(r);
28765         });
28766         for(var r = 0 ; r < table.length; r++) {
28767             var re = tab.rows[r];
28768             
28769             var re = tab.ownerDocument.createElement('tr');
28770             ctr.appendChild(re);
28771             for(var c = 0 ; c < table[r].length; c++) {
28772                 if (table[r][c].cell === false) {
28773                     continue;
28774                 }
28775                 
28776                 re.appendChild(table[r][c].cell);
28777                  
28778                 table[r][c].cell = false;
28779             }
28780         }
28781         
28782     },
28783     updateWidths : function(table)
28784     {
28785         for(var r = 0 ; r < table.length; r++) {
28786            
28787             for(var c = 0 ; c < table[r].length; c++) {
28788                 if (table[r][c].cell === false) {
28789                     continue;
28790                 }
28791                 
28792                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28793                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28794                     el.width = Math.floor(this.colWidths[c])  +'%';
28795                     el.updateElement(el.node);
28796                 }
28797                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
28798                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28799                     var width = 0;
28800                     for(var i = 0; i < table[r][c].colspan; i ++) {
28801                         width += Math.floor(this.colWidths[c + i]);
28802                     }
28803                     el.width = width  +'%';
28804                     el.updateElement(el.node);
28805                 }
28806                 table[r][c].cell = false; // done
28807             }
28808         }
28809     },
28810     normalizeWidths : function(table)
28811     {
28812         if (this.colWidths[0] === false) {
28813             var nw = 100.0 / this.colWidths.length;
28814             this.colWidths.forEach(function(w,i) {
28815                 this.colWidths[i] = nw;
28816             },this);
28817             return;
28818         }
28819     
28820         var t = 0, missing = [];
28821         
28822         this.colWidths.forEach(function(w,i) {
28823             //if you mix % and
28824             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28825             var add =  this.colWidths[i];
28826             if (add > 0) {
28827                 t+=add;
28828                 return;
28829             }
28830             missing.push(i);
28831             
28832             
28833         },this);
28834         var nc = this.colWidths.length;
28835         if (missing.length) {
28836             var mult = (nc - missing.length) / (1.0 * nc);
28837             var t = mult * t;
28838             var ew = (100 -t) / (1.0 * missing.length);
28839             this.colWidths.forEach(function(w,i) {
28840                 if (w > 0) {
28841                     this.colWidths[i] = w * mult;
28842                     return;
28843                 }
28844                 
28845                 this.colWidths[i] = ew;
28846             }, this);
28847             // have to make up numbers..
28848              
28849         }
28850         // now we should have all the widths..
28851         
28852     
28853     },
28854     
28855     shrinkColumn : function()
28856     {
28857         var table = this.toTableArray();
28858         this.normalizeWidths(table);
28859         var col = this.cellData.col;
28860         var nw = this.colWidths[col] * 0.8;
28861         if (nw < 5) {
28862             return;
28863         }
28864         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28865         this.colWidths.forEach(function(w,i) {
28866             if (i == col) {
28867                  this.colWidths[i] = nw;
28868                 return;
28869             }
28870             this.colWidths[i] += otherAdd
28871         }, this);
28872         this.updateWidths(table);
28873          
28874     },
28875     growColumn : function()
28876     {
28877         var table = this.toTableArray();
28878         this.normalizeWidths(table);
28879         var col = this.cellData.col;
28880         var nw = this.colWidths[col] * 1.2;
28881         if (nw > 90) {
28882             return;
28883         }
28884         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28885         this.colWidths.forEach(function(w,i) {
28886             if (i == col) {
28887                 this.colWidths[i] = nw;
28888                 return;
28889             }
28890             this.colWidths[i] -= otherSub
28891         }, this);
28892         this.updateWidths(table);
28893          
28894     },
28895     deleteRow : function()
28896     {
28897         // delete this rows 'tr'
28898         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28899         // then reduce the rowspan.
28900         var table = this.toTableArray();
28901         // this.cellData.row;
28902         for (var i =0;i< table[this.cellData.row].length ; i++) {
28903             var c = table[this.cellData.row][i];
28904             if (c.row != this.cellData.row) {
28905                 
28906                 c.rowspan--;
28907                 c.cell.setAttribute('rowspan', c.rowspan);
28908                 continue;
28909             }
28910             if (c.rowspan > 1) {
28911                 c.rowspan--;
28912                 c.cell.setAttribute('rowspan', c.rowspan);
28913             }
28914         }
28915         table.splice(this.cellData.row,1);
28916         this.redrawAllCells(table);
28917         
28918     },
28919     deleteColumn : function()
28920     {
28921         var table = this.toTableArray();
28922         
28923         for (var i =0;i< table.length ; i++) {
28924             var c = table[i][this.cellData.col];
28925             if (c.col != this.cellData.col) {
28926                 table[i][this.cellData.col].colspan--;
28927             } else if (c.colspan > 1) {
28928                 c.colspan--;
28929                 c.cell.setAttribute('colspan', c.colspan);
28930             }
28931             table[i].splice(this.cellData.col,1);
28932         }
28933         
28934         this.redrawAllCells(table);
28935     }
28936     
28937     
28938     
28939     
28940 })
28941
28942 //<script type="text/javascript">
28943
28944 /*
28945  * Based  Ext JS Library 1.1.1
28946  * Copyright(c) 2006-2007, Ext JS, LLC.
28947  * LGPL
28948  *
28949  */
28950  
28951 /**
28952  * @class Roo.HtmlEditorCore
28953  * @extends Roo.Component
28954  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28955  *
28956  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28957  */
28958
28959 Roo.HtmlEditorCore = function(config){
28960     
28961     
28962     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28963     
28964     
28965     this.addEvents({
28966         /**
28967          * @event initialize
28968          * Fires when the editor is fully initialized (including the iframe)
28969          * @param {Roo.HtmlEditorCore} this
28970          */
28971         initialize: true,
28972         /**
28973          * @event activate
28974          * Fires when the editor is first receives the focus. Any insertion must wait
28975          * until after this event.
28976          * @param {Roo.HtmlEditorCore} this
28977          */
28978         activate: true,
28979          /**
28980          * @event beforesync
28981          * Fires before the textarea is updated with content from the editor iframe. Return false
28982          * to cancel the sync.
28983          * @param {Roo.HtmlEditorCore} this
28984          * @param {String} html
28985          */
28986         beforesync: true,
28987          /**
28988          * @event beforepush
28989          * Fires before the iframe editor is updated with content from the textarea. Return false
28990          * to cancel the push.
28991          * @param {Roo.HtmlEditorCore} this
28992          * @param {String} html
28993          */
28994         beforepush: true,
28995          /**
28996          * @event sync
28997          * Fires when the textarea is updated with content from the editor iframe.
28998          * @param {Roo.HtmlEditorCore} this
28999          * @param {String} html
29000          */
29001         sync: true,
29002          /**
29003          * @event push
29004          * Fires when the iframe editor is updated with content from the textarea.
29005          * @param {Roo.HtmlEditorCore} this
29006          * @param {String} html
29007          */
29008         push: true,
29009         
29010         /**
29011          * @event editorevent
29012          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
29013          * @param {Roo.HtmlEditorCore} this
29014          */
29015         editorevent: true 
29016          
29017         
29018     });
29019     
29020     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
29021     
29022     // defaults : white / black...
29023     this.applyBlacklists();
29024     
29025     
29026     
29027 };
29028
29029
29030 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
29031
29032
29033      /**
29034      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
29035      */
29036     
29037     owner : false,
29038     
29039      /**
29040      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
29041      *                        Roo.resizable.
29042      */
29043     resizable : false,
29044      /**
29045      * @cfg {Number} height (in pixels)
29046      */   
29047     height: 300,
29048    /**
29049      * @cfg {Number} width (in pixels)
29050      */   
29051     width: 500,
29052      /**
29053      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
29054      *         if you are doing an email editor, this probably needs disabling, it's designed
29055      */
29056     autoClean: true,
29057     
29058     /**
29059      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
29060      */
29061     enableBlocks : true,
29062     /**
29063      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
29064      * 
29065      */
29066     stylesheets: false,
29067      /**
29068      * @cfg {String} language default en - language of text (usefull for rtl languages)
29069      * 
29070      */
29071     language: 'en',
29072     
29073     /**
29074      * @cfg {boolean} allowComments - default false - allow comments in HTML source
29075      *          - by default they are stripped - if you are editing email you may need this.
29076      */
29077     allowComments: false,
29078     // id of frame..
29079     frameId: false,
29080     
29081     // private properties
29082     validationEvent : false,
29083     deferHeight: true,
29084     initialized : false,
29085     activated : false,
29086     sourceEditMode : false,
29087     onFocus : Roo.emptyFn,
29088     iframePad:3,
29089     hideMode:'offsets',
29090     
29091     clearUp: true,
29092     
29093     // blacklist + whitelisted elements..
29094     black: false,
29095     white: false,
29096      
29097     bodyCls : '',
29098
29099     
29100     undoManager : false,
29101     /**
29102      * Protected method that will not generally be called directly. It
29103      * is called when the editor initializes the iframe with HTML contents. Override this method if you
29104      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
29105      */
29106     getDocMarkup : function(){
29107         // body styles..
29108         var st = '';
29109         
29110         // inherit styels from page...?? 
29111         if (this.stylesheets === false) {
29112             
29113             Roo.get(document.head).select('style').each(function(node) {
29114                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29115             });
29116             
29117             Roo.get(document.head).select('link').each(function(node) { 
29118                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29119             });
29120             
29121         } else if (!this.stylesheets.length) {
29122                 // simple..
29123                 st = '<style type="text/css">' +
29124                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29125                    '</style>';
29126         } else {
29127             for (var i in this.stylesheets) {
29128                 if (typeof(this.stylesheets[i]) != 'string') {
29129                     continue;
29130                 }
29131                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
29132             }
29133             
29134         }
29135         
29136         st +=  '<style type="text/css">' +
29137             'IMG { cursor: pointer } ' +
29138         '</style>';
29139         
29140         st += '<meta name="google" content="notranslate">';
29141         
29142         var cls = 'notranslate roo-htmleditor-body';
29143         
29144         if(this.bodyCls.length){
29145             cls += ' ' + this.bodyCls;
29146         }
29147         
29148         return '<html  class="notranslate" translate="no"><head>' + st  +
29149             //<style type="text/css">' +
29150             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29151             //'</style>' +
29152             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
29153     },
29154
29155     // private
29156     onRender : function(ct, position)
29157     {
29158         var _t = this;
29159         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
29160         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
29161         
29162         
29163         this.el.dom.style.border = '0 none';
29164         this.el.dom.setAttribute('tabIndex', -1);
29165         this.el.addClass('x-hidden hide');
29166         
29167         
29168         
29169         if(Roo.isIE){ // fix IE 1px bogus margin
29170             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29171         }
29172        
29173         
29174         this.frameId = Roo.id();
29175         
29176          
29177         
29178         var iframe = this.owner.wrap.createChild({
29179             tag: 'iframe',
29180             cls: 'form-control', // bootstrap..
29181             id: this.frameId,
29182             name: this.frameId,
29183             frameBorder : 'no',
29184             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29185         }, this.el
29186         );
29187         
29188         
29189         this.iframe = iframe.dom;
29190
29191         this.assignDocWin();
29192         
29193         this.doc.designMode = 'on';
29194        
29195         this.doc.open();
29196         this.doc.write(this.getDocMarkup());
29197         this.doc.close();
29198
29199         
29200         var task = { // must defer to wait for browser to be ready
29201             run : function(){
29202                 //console.log("run task?" + this.doc.readyState);
29203                 this.assignDocWin();
29204                 if(this.doc.body || this.doc.readyState == 'complete'){
29205                     try {
29206                         this.doc.designMode="on";
29207                         
29208                     } catch (e) {
29209                         return;
29210                     }
29211                     Roo.TaskMgr.stop(task);
29212                     this.initEditor.defer(10, this);
29213                 }
29214             },
29215             interval : 10,
29216             duration: 10000,
29217             scope: this
29218         };
29219         Roo.TaskMgr.start(task);
29220
29221     },
29222
29223     // private
29224     onResize : function(w, h)
29225     {
29226          Roo.log('resize: ' +w + ',' + h );
29227         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29228         if(!this.iframe){
29229             return;
29230         }
29231         if(typeof w == 'number'){
29232             
29233             this.iframe.style.width = w + 'px';
29234         }
29235         if(typeof h == 'number'){
29236             
29237             this.iframe.style.height = h + 'px';
29238             if(this.doc){
29239                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29240             }
29241         }
29242         
29243     },
29244
29245     /**
29246      * Toggles the editor between standard and source edit mode.
29247      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29248      */
29249     toggleSourceEdit : function(sourceEditMode){
29250         
29251         this.sourceEditMode = sourceEditMode === true;
29252         
29253         if(this.sourceEditMode){
29254  
29255             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29256             
29257         }else{
29258             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29259             //this.iframe.className = '';
29260             this.deferFocus();
29261         }
29262         //this.setSize(this.owner.wrap.getSize());
29263         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29264     },
29265
29266     
29267   
29268
29269     /**
29270      * Protected method that will not generally be called directly. If you need/want
29271      * custom HTML cleanup, this is the method you should override.
29272      * @param {String} html The HTML to be cleaned
29273      * return {String} The cleaned HTML
29274      */
29275     cleanHtml : function(html)
29276     {
29277         html = String(html);
29278         if(html.length > 5){
29279             if(Roo.isSafari){ // strip safari nonsense
29280                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29281             }
29282         }
29283         if(html == '&nbsp;'){
29284             html = '';
29285         }
29286         return html;
29287     },
29288
29289     /**
29290      * HTML Editor -> Textarea
29291      * Protected method that will not generally be called directly. Syncs the contents
29292      * of the editor iframe with the textarea.
29293      */
29294     syncValue : function()
29295     {
29296         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29297         if(this.initialized){
29298             
29299             if (this.undoManager) {
29300                 this.undoManager.addEvent();
29301             }
29302
29303             
29304             var bd = (this.doc.body || this.doc.documentElement);
29305            
29306             
29307             var sel = this.win.getSelection();
29308             
29309             var div = document.createElement('div');
29310             div.innerHTML = bd.innerHTML;
29311             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29312             if (gtx.length > 0) {
29313                 var rm = gtx.item(0).parentNode;
29314                 rm.parentNode.removeChild(rm);
29315             }
29316             
29317            
29318             if (this.enableBlocks) {
29319                 new Roo.htmleditor.FilterBlock({ node : div });
29320             }
29321             
29322             var html = div.innerHTML;
29323             
29324             //?? tidy?
29325             if (this.autoClean) {
29326                 
29327                 new Roo.htmleditor.FilterAttributes({
29328                     node : div,
29329                     attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29330                     attrib_clean : ['href', 'src' ] 
29331                 });
29332                 
29333                 var tidy = new Roo.htmleditor.TidySerializer({
29334                     inner:  true
29335                 });
29336                 html  = tidy.serialize(div);
29337                 
29338             }
29339             
29340             
29341             if(Roo.isSafari){
29342                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29343                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29344                 if(m && m[1]){
29345                     html = '<div style="'+m[0]+'">' + html + '</div>';
29346                 }
29347             }
29348             html = this.cleanHtml(html);
29349             // fix up the special chars.. normaly like back quotes in word...
29350             // however we do not want to do this with chinese..
29351             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29352                 
29353                 var cc = match.charCodeAt();
29354
29355                 // Get the character value, handling surrogate pairs
29356                 if (match.length == 2) {
29357                     // It's a surrogate pair, calculate the Unicode code point
29358                     var high = match.charCodeAt(0) - 0xD800;
29359                     var low  = match.charCodeAt(1) - 0xDC00;
29360                     cc = (high * 0x400) + low + 0x10000;
29361                 }  else if (
29362                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29363                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29364                     (cc >= 0xf900 && cc < 0xfb00 )
29365                 ) {
29366                         return match;
29367                 }  
29368          
29369                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29370                 return "&#" + cc + ";";
29371                 
29372                 
29373             });
29374             
29375             
29376              
29377             if(this.owner.fireEvent('beforesync', this, html) !== false){
29378                 this.el.dom.value = html;
29379                 this.owner.fireEvent('sync', this, html);
29380             }
29381         }
29382     },
29383
29384     /**
29385      * TEXTAREA -> EDITABLE
29386      * Protected method that will not generally be called directly. Pushes the value of the textarea
29387      * into the iframe editor.
29388      */
29389     pushValue : function()
29390     {
29391         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29392         if(this.initialized){
29393             var v = this.el.dom.value.trim();
29394             
29395             
29396             if(this.owner.fireEvent('beforepush', this, v) !== false){
29397                 var d = (this.doc.body || this.doc.documentElement);
29398                 d.innerHTML = v;
29399                  
29400                 this.el.dom.value = d.innerHTML;
29401                 this.owner.fireEvent('push', this, v);
29402             }
29403             if (this.autoClean) {
29404                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29405                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29406             }
29407             if (this.enableBlocks) {
29408                 Roo.htmleditor.Block.initAll(this.doc.body);
29409             }
29410             
29411             this.updateLanguage();
29412             
29413             var lc = this.doc.body.lastChild;
29414             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29415                 // add an extra line at the end.
29416                 this.doc.body.appendChild(this.doc.createElement('br'));
29417             }
29418             
29419             
29420         }
29421     },
29422
29423     // private
29424     deferFocus : function(){
29425         this.focus.defer(10, this);
29426     },
29427
29428     // doc'ed in Field
29429     focus : function(){
29430         if(this.win && !this.sourceEditMode){
29431             this.win.focus();
29432         }else{
29433             this.el.focus();
29434         }
29435     },
29436     
29437     assignDocWin: function()
29438     {
29439         var iframe = this.iframe;
29440         
29441          if(Roo.isIE){
29442             this.doc = iframe.contentWindow.document;
29443             this.win = iframe.contentWindow;
29444         } else {
29445 //            if (!Roo.get(this.frameId)) {
29446 //                return;
29447 //            }
29448 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29449 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29450             
29451             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29452                 return;
29453             }
29454             
29455             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29456             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29457         }
29458     },
29459     
29460     // private
29461     initEditor : function(){
29462         //console.log("INIT EDITOR");
29463         this.assignDocWin();
29464         
29465         
29466         
29467         this.doc.designMode="on";
29468         this.doc.open();
29469         this.doc.write(this.getDocMarkup());
29470         this.doc.close();
29471         
29472         var dbody = (this.doc.body || this.doc.documentElement);
29473         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29474         // this copies styles from the containing element into thsi one..
29475         // not sure why we need all of this..
29476         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29477         
29478         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29479         //ss['background-attachment'] = 'fixed'; // w3c
29480         dbody.bgProperties = 'fixed'; // ie
29481         dbody.setAttribute("translate", "no");
29482         
29483         //Roo.DomHelper.applyStyles(dbody, ss);
29484         Roo.EventManager.on(this.doc, {
29485              
29486             'mouseup': this.onEditorEvent,
29487             'dblclick': this.onEditorEvent,
29488             'click': this.onEditorEvent,
29489             'keyup': this.onEditorEvent,
29490             
29491             buffer:100,
29492             scope: this
29493         });
29494         Roo.EventManager.on(this.doc, {
29495             'paste': this.onPasteEvent,
29496             scope : this
29497         });
29498         if(Roo.isGecko){
29499             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29500         }
29501         //??? needed???
29502         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29503             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29504         }
29505         this.initialized = true;
29506
29507         
29508         // initialize special key events - enter
29509         new Roo.htmleditor.KeyEnter({core : this});
29510         
29511          
29512         
29513         this.owner.fireEvent('initialize', this);
29514         this.pushValue();
29515     },
29516     // this is to prevent a href clicks resulting in a redirect?
29517    
29518     onPasteEvent : function(e,v)
29519     {
29520         // I think we better assume paste is going to be a dirty load of rubish from word..
29521         
29522         // even pasting into a 'email version' of this widget will have to clean up that mess.
29523         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29524         
29525         // check what type of paste - if it's an image, then handle it differently.
29526         if (cd.files && cd.files.length > 0) {
29527             // pasting images?
29528             var urlAPI = (window.createObjectURL && window) || 
29529                 (window.URL && URL.revokeObjectURL && URL) || 
29530                 (window.webkitURL && webkitURL);
29531     
29532             var url = urlAPI.createObjectURL( cd.files[0]);
29533             this.insertAtCursor('<img src=" + url + ">');
29534             return false;
29535         }
29536         if (cd.types.indexOf('text/html') < 0 ) {
29537             return false;
29538         }
29539         var images = [];
29540         var html = cd.getData('text/html'); // clipboard event
29541         if (cd.types.indexOf('text/rtf') > -1) {
29542             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29543             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29544         }
29545         //Roo.log(images);
29546         //Roo.log(imgs);
29547         // fixme..
29548         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29549                        .map(function(g) { return g.toDataURL(); })
29550                        .filter(function(g) { return g != 'about:blank'; });
29551         
29552         //Roo.log(html);
29553         html = this.cleanWordChars(html);
29554         
29555         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29556         
29557         
29558         var sn = this.getParentElement();
29559         // check if d contains a table, and prevent nesting??
29560         //Roo.log(d.getElementsByTagName('table'));
29561         //Roo.log(sn);
29562         //Roo.log(sn.closest('table'));
29563         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29564             e.preventDefault();
29565             this.insertAtCursor("You can not nest tables");
29566             //Roo.log("prevent?"); // fixme - 
29567             return false;
29568         }
29569         
29570         
29571         
29572         if (images.length > 0) {
29573             // replace all v:imagedata - with img.
29574             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
29575             Roo.each(ar, function(node) {
29576                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
29577                 node.parentNode.removeChild(node);
29578             });
29579             
29580             
29581             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29582                 img.setAttribute('src', images[i]);
29583             });
29584         }
29585         if (this.autoClean) {
29586             new Roo.htmleditor.FilterWord({ node : d });
29587             
29588             new Roo.htmleditor.FilterStyleToTag({ node : d });
29589             new Roo.htmleditor.FilterAttributes({
29590                 node : d,
29591                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29592                 attrib_clean : ['href', 'src' ] 
29593             });
29594             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29595             // should be fonts..
29596             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
29597             new Roo.htmleditor.FilterParagraph({ node : d });
29598             new Roo.htmleditor.FilterSpan({ node : d });
29599             new Roo.htmleditor.FilterLongBr({ node : d });
29600             new Roo.htmleditor.FilterComment({ node : d });
29601             
29602             
29603         }
29604         if (this.enableBlocks) {
29605                 
29606             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29607                 if (img.closest('figure')) { // assume!! that it's aready
29608                     return;
29609                 }
29610                 var fig  = new Roo.htmleditor.BlockFigure({
29611                     image_src  : img.src
29612                 });
29613                 fig.updateElement(img); // replace it..
29614                 
29615             });
29616         }
29617         
29618         
29619         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29620         if (this.enableBlocks) {
29621             Roo.htmleditor.Block.initAll(this.doc.body);
29622         }
29623          
29624         
29625         e.preventDefault();
29626         return false;
29627         // default behaveiour should be our local cleanup paste? (optional?)
29628         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29629         //this.owner.fireEvent('paste', e, v);
29630     },
29631     // private
29632     onDestroy : function(){
29633         
29634         
29635         
29636         if(this.rendered){
29637             
29638             //for (var i =0; i < this.toolbars.length;i++) {
29639             //    // fixme - ask toolbars for heights?
29640             //    this.toolbars[i].onDestroy();
29641            // }
29642             
29643             //this.wrap.dom.innerHTML = '';
29644             //this.wrap.remove();
29645         }
29646     },
29647
29648     // private
29649     onFirstFocus : function(){
29650         
29651         this.assignDocWin();
29652         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29653         
29654         this.activated = true;
29655          
29656     
29657         if(Roo.isGecko){ // prevent silly gecko errors
29658             this.win.focus();
29659             var s = this.win.getSelection();
29660             if(!s.focusNode || s.focusNode.nodeType != 3){
29661                 var r = s.getRangeAt(0);
29662                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29663                 r.collapse(true);
29664                 this.deferFocus();
29665             }
29666             try{
29667                 this.execCmd('useCSS', true);
29668                 this.execCmd('styleWithCSS', false);
29669             }catch(e){}
29670         }
29671         this.owner.fireEvent('activate', this);
29672     },
29673
29674     // private
29675     adjustFont: function(btn){
29676         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29677         //if(Roo.isSafari){ // safari
29678         //    adjust *= 2;
29679        // }
29680         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29681         if(Roo.isSafari){ // safari
29682             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29683             v =  (v < 10) ? 10 : v;
29684             v =  (v > 48) ? 48 : v;
29685             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29686             
29687         }
29688         
29689         
29690         v = Math.max(1, v+adjust);
29691         
29692         this.execCmd('FontSize', v  );
29693     },
29694
29695     onEditorEvent : function(e)
29696     {
29697          
29698         
29699         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29700             return; // we do not handle this.. (undo manager does..)
29701         }
29702         // in theory this detects if the last element is not a br, then we try and do that.
29703         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29704         if (e &&
29705             e.target.nodeName == 'BODY' &&
29706             e.type == "mouseup" &&
29707             this.doc.body.lastChild
29708            ) {
29709             var lc = this.doc.body.lastChild;
29710             // gtx-trans is google translate plugin adding crap.
29711             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29712                 lc = lc.previousSibling;
29713             }
29714             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29715             // if last element is <BR> - then dont do anything.
29716             
29717                 var ns = this.doc.createElement('br');
29718                 this.doc.body.appendChild(ns);
29719                 range = this.doc.createRange();
29720                 range.setStartAfter(ns);
29721                 range.collapse(true);
29722                 var sel = this.win.getSelection();
29723                 sel.removeAllRanges();
29724                 sel.addRange(range);
29725             }
29726         }
29727         
29728         
29729         
29730         this.fireEditorEvent(e);
29731       //  this.updateToolbar();
29732         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29733     },
29734     
29735     fireEditorEvent: function(e)
29736     {
29737         this.owner.fireEvent('editorevent', this, e);
29738     },
29739
29740     insertTag : function(tg)
29741     {
29742         // could be a bit smarter... -> wrap the current selected tRoo..
29743         if (tg.toLowerCase() == 'span' ||
29744             tg.toLowerCase() == 'code' ||
29745             tg.toLowerCase() == 'sup' ||
29746             tg.toLowerCase() == 'sub' 
29747             ) {
29748             
29749             range = this.createRange(this.getSelection());
29750             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29751             wrappingNode.appendChild(range.extractContents());
29752             range.insertNode(wrappingNode);
29753
29754             return;
29755             
29756             
29757             
29758         }
29759         this.execCmd("formatblock",   tg);
29760         this.undoManager.addEvent(); 
29761     },
29762     
29763     insertText : function(txt)
29764     {
29765         
29766         
29767         var range = this.createRange();
29768         range.deleteContents();
29769                //alert(Sender.getAttribute('label'));
29770                
29771         range.insertNode(this.doc.createTextNode(txt));
29772         this.undoManager.addEvent();
29773     } ,
29774     
29775      
29776
29777     /**
29778      * Executes a Midas editor command on the editor document and performs necessary focus and
29779      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29780      * @param {String} cmd The Midas command
29781      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29782      */
29783     relayCmd : function(cmd, value)
29784     {
29785         
29786         switch (cmd) {
29787             case 'justifyleft':
29788             case 'justifyright':
29789             case 'justifycenter':
29790                 // if we are in a cell, then we will adjust the
29791                 var n = this.getParentElement();
29792                 var td = n.closest('td');
29793                 if (td) {
29794                     var bl = Roo.htmleditor.Block.factory(td);
29795                     bl.textAlign = cmd.replace('justify','');
29796                     bl.updateElement();
29797                     this.owner.fireEvent('editorevent', this);
29798                     return;
29799                 }
29800                 this.execCmd('styleWithCSS', true); // 
29801                 break;
29802             case 'bold':
29803             case 'italic':
29804                 // if there is no selection, then we insert, and set the curson inside it..
29805                 this.execCmd('styleWithCSS', false); 
29806                 break;
29807                 
29808         
29809             default:
29810                 break;
29811         }
29812         
29813         
29814         this.win.focus();
29815         this.execCmd(cmd, value);
29816         this.owner.fireEvent('editorevent', this);
29817         //this.updateToolbar();
29818         this.owner.deferFocus();
29819     },
29820
29821     /**
29822      * Executes a Midas editor command directly on the editor document.
29823      * For visual commands, you should use {@link #relayCmd} instead.
29824      * <b>This should only be called after the editor is initialized.</b>
29825      * @param {String} cmd The Midas command
29826      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29827      */
29828     execCmd : function(cmd, value){
29829         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29830         this.syncValue();
29831     },
29832  
29833  
29834    
29835     /**
29836      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29837      * to insert tRoo.
29838      * @param {String} text | dom node.. 
29839      */
29840     insertAtCursor : function(text)
29841     {
29842         
29843         if(!this.activated){
29844             return;
29845         }
29846          
29847         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29848             this.win.focus();
29849             
29850             
29851             // from jquery ui (MIT licenced)
29852             var range, node;
29853             var win = this.win;
29854             
29855             if (win.getSelection && win.getSelection().getRangeAt) {
29856                 
29857                 // delete the existing?
29858                 
29859                 this.createRange(this.getSelection()).deleteContents();
29860                 range = win.getSelection().getRangeAt(0);
29861                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29862                 range.insertNode(node);
29863                 range = range.cloneRange();
29864                 range.collapse(false);
29865                  
29866                 win.getSelection().removeAllRanges();
29867                 win.getSelection().addRange(range);
29868                 
29869                 
29870                 
29871             } else if (win.document.selection && win.document.selection.createRange) {
29872                 // no firefox support
29873                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29874                 win.document.selection.createRange().pasteHTML(txt);
29875             
29876             } else {
29877                 // no firefox support
29878                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29879                 this.execCmd('InsertHTML', txt);
29880             } 
29881             this.syncValue();
29882             
29883             this.deferFocus();
29884         }
29885     },
29886  // private
29887     mozKeyPress : function(e){
29888         if(e.ctrlKey){
29889             var c = e.getCharCode(), cmd;
29890           
29891             if(c > 0){
29892                 c = String.fromCharCode(c).toLowerCase();
29893                 switch(c){
29894                     case 'b':
29895                         cmd = 'bold';
29896                         break;
29897                     case 'i':
29898                         cmd = 'italic';
29899                         break;
29900                     
29901                     case 'u':
29902                         cmd = 'underline';
29903                         break;
29904                     
29905                     //case 'v':
29906                       //  this.cleanUpPaste.defer(100, this);
29907                       //  return;
29908                         
29909                 }
29910                 if(cmd){
29911                     
29912                     this.relayCmd(cmd);
29913                     //this.win.focus();
29914                     //this.execCmd(cmd);
29915                     //this.deferFocus();
29916                     e.preventDefault();
29917                 }
29918                 
29919             }
29920         }
29921     },
29922
29923     // private
29924     fixKeys : function(){ // load time branching for fastest keydown performance
29925         
29926         
29927         if(Roo.isIE){
29928             return function(e){
29929                 var k = e.getKey(), r;
29930                 if(k == e.TAB){
29931                     e.stopEvent();
29932                     r = this.doc.selection.createRange();
29933                     if(r){
29934                         r.collapse(true);
29935                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29936                         this.deferFocus();
29937                     }
29938                     return;
29939                 }
29940                 /// this is handled by Roo.htmleditor.KeyEnter
29941                  /*
29942                 if(k == e.ENTER){
29943                     r = this.doc.selection.createRange();
29944                     if(r){
29945                         var target = r.parentElement();
29946                         if(!target || target.tagName.toLowerCase() != 'li'){
29947                             e.stopEvent();
29948                             r.pasteHTML('<br/>');
29949                             r.collapse(false);
29950                             r.select();
29951                         }
29952                     }
29953                 }
29954                 */
29955                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29956                 //    this.cleanUpPaste.defer(100, this);
29957                 //    return;
29958                 //}
29959                 
29960                 
29961             };
29962         }else if(Roo.isOpera){
29963             return function(e){
29964                 var k = e.getKey();
29965                 if(k == e.TAB){
29966                     e.stopEvent();
29967                     this.win.focus();
29968                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29969                     this.deferFocus();
29970                 }
29971                
29972                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29973                 //    this.cleanUpPaste.defer(100, this);
29974                  //   return;
29975                 //}
29976                 
29977             };
29978         }else if(Roo.isSafari){
29979             return function(e){
29980                 var k = e.getKey();
29981                 
29982                 if(k == e.TAB){
29983                     e.stopEvent();
29984                     this.execCmd('InsertText','\t');
29985                     this.deferFocus();
29986                     return;
29987                 }
29988                  this.mozKeyPress(e);
29989                 
29990                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29991                  //   this.cleanUpPaste.defer(100, this);
29992                  //   return;
29993                // }
29994                 
29995              };
29996         }
29997     }(),
29998     
29999     getAllAncestors: function()
30000     {
30001         var p = this.getSelectedNode();
30002         var a = [];
30003         if (!p) {
30004             a.push(p); // push blank onto stack..
30005             p = this.getParentElement();
30006         }
30007         
30008         
30009         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
30010             a.push(p);
30011             p = p.parentNode;
30012         }
30013         a.push(this.doc.body);
30014         return a;
30015     },
30016     lastSel : false,
30017     lastSelNode : false,
30018     
30019     
30020     getSelection : function() 
30021     {
30022         this.assignDocWin();
30023         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
30024     },
30025     /**
30026      * Select a dom node
30027      * @param {DomElement} node the node to select
30028      */
30029     selectNode : function(node, collapse)
30030     {
30031         var nodeRange = node.ownerDocument.createRange();
30032         try {
30033             nodeRange.selectNode(node);
30034         } catch (e) {
30035             nodeRange.selectNodeContents(node);
30036         }
30037         if (collapse === true) {
30038             nodeRange.collapse(true);
30039         }
30040         //
30041         var s = this.win.getSelection();
30042         s.removeAllRanges();
30043         s.addRange(nodeRange);
30044     },
30045     
30046     getSelectedNode: function() 
30047     {
30048         // this may only work on Gecko!!!
30049         
30050         // should we cache this!!!!
30051         
30052          
30053          
30054         var range = this.createRange(this.getSelection()).cloneRange();
30055         
30056         if (Roo.isIE) {
30057             var parent = range.parentElement();
30058             while (true) {
30059                 var testRange = range.duplicate();
30060                 testRange.moveToElementText(parent);
30061                 if (testRange.inRange(range)) {
30062                     break;
30063                 }
30064                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
30065                     break;
30066                 }
30067                 parent = parent.parentElement;
30068             }
30069             return parent;
30070         }
30071         
30072         // is ancestor a text element.
30073         var ac =  range.commonAncestorContainer;
30074         if (ac.nodeType == 3) {
30075             ac = ac.parentNode;
30076         }
30077         
30078         var ar = ac.childNodes;
30079          
30080         var nodes = [];
30081         var other_nodes = [];
30082         var has_other_nodes = false;
30083         for (var i=0;i<ar.length;i++) {
30084             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
30085                 continue;
30086             }
30087             // fullly contained node.
30088             
30089             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
30090                 nodes.push(ar[i]);
30091                 continue;
30092             }
30093             
30094             // probably selected..
30095             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
30096                 other_nodes.push(ar[i]);
30097                 continue;
30098             }
30099             // outer..
30100             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
30101                 continue;
30102             }
30103             
30104             
30105             has_other_nodes = true;
30106         }
30107         if (!nodes.length && other_nodes.length) {
30108             nodes= other_nodes;
30109         }
30110         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
30111             return false;
30112         }
30113         
30114         return nodes[0];
30115     },
30116     
30117     
30118     createRange: function(sel)
30119     {
30120         // this has strange effects when using with 
30121         // top toolbar - not sure if it's a great idea.
30122         //this.editor.contentWindow.focus();
30123         if (typeof sel != "undefined") {
30124             try {
30125                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
30126             } catch(e) {
30127                 return this.doc.createRange();
30128             }
30129         } else {
30130             return this.doc.createRange();
30131         }
30132     },
30133     getParentElement: function()
30134     {
30135         
30136         this.assignDocWin();
30137         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
30138         
30139         var range = this.createRange(sel);
30140          
30141         try {
30142             var p = range.commonAncestorContainer;
30143             while (p.nodeType == 3) { // text node
30144                 p = p.parentNode;
30145             }
30146             return p;
30147         } catch (e) {
30148             return null;
30149         }
30150     
30151     },
30152     /***
30153      *
30154      * Range intersection.. the hard stuff...
30155      *  '-1' = before
30156      *  '0' = hits..
30157      *  '1' = after.
30158      *         [ -- selected range --- ]
30159      *   [fail]                        [fail]
30160      *
30161      *    basically..
30162      *      if end is before start or  hits it. fail.
30163      *      if start is after end or hits it fail.
30164      *
30165      *   if either hits (but other is outside. - then it's not 
30166      *   
30167      *    
30168      **/
30169     
30170     
30171     // @see http://www.thismuchiknow.co.uk/?p=64.
30172     rangeIntersectsNode : function(range, node)
30173     {
30174         var nodeRange = node.ownerDocument.createRange();
30175         try {
30176             nodeRange.selectNode(node);
30177         } catch (e) {
30178             nodeRange.selectNodeContents(node);
30179         }
30180     
30181         var rangeStartRange = range.cloneRange();
30182         rangeStartRange.collapse(true);
30183     
30184         var rangeEndRange = range.cloneRange();
30185         rangeEndRange.collapse(false);
30186     
30187         var nodeStartRange = nodeRange.cloneRange();
30188         nodeStartRange.collapse(true);
30189     
30190         var nodeEndRange = nodeRange.cloneRange();
30191         nodeEndRange.collapse(false);
30192     
30193         return rangeStartRange.compareBoundaryPoints(
30194                  Range.START_TO_START, nodeEndRange) == -1 &&
30195                rangeEndRange.compareBoundaryPoints(
30196                  Range.START_TO_START, nodeStartRange) == 1;
30197         
30198          
30199     },
30200     rangeCompareNode : function(range, node)
30201     {
30202         var nodeRange = node.ownerDocument.createRange();
30203         try {
30204             nodeRange.selectNode(node);
30205         } catch (e) {
30206             nodeRange.selectNodeContents(node);
30207         }
30208         
30209         
30210         range.collapse(true);
30211     
30212         nodeRange.collapse(true);
30213      
30214         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30215         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30216          
30217         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30218         
30219         var nodeIsBefore   =  ss == 1;
30220         var nodeIsAfter    = ee == -1;
30221         
30222         if (nodeIsBefore && nodeIsAfter) {
30223             return 0; // outer
30224         }
30225         if (!nodeIsBefore && nodeIsAfter) {
30226             return 1; //right trailed.
30227         }
30228         
30229         if (nodeIsBefore && !nodeIsAfter) {
30230             return 2;  // left trailed.
30231         }
30232         // fully contined.
30233         return 3;
30234     },
30235  
30236     cleanWordChars : function(input) {// change the chars to hex code
30237         
30238        var swapCodes  = [ 
30239             [    8211, "&#8211;" ], 
30240             [    8212, "&#8212;" ], 
30241             [    8216,  "'" ],  
30242             [    8217, "'" ],  
30243             [    8220, '"' ],  
30244             [    8221, '"' ],  
30245             [    8226, "*" ],  
30246             [    8230, "..." ]
30247         ]; 
30248         var output = input;
30249         Roo.each(swapCodes, function(sw) { 
30250             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30251             
30252             output = output.replace(swapper, sw[1]);
30253         });
30254         
30255         return output;
30256     },
30257     
30258      
30259     
30260         
30261     
30262     cleanUpChild : function (node)
30263     {
30264         
30265         new Roo.htmleditor.FilterComment({node : node});
30266         new Roo.htmleditor.FilterAttributes({
30267                 node : node,
30268                 attrib_black : this.ablack,
30269                 attrib_clean : this.aclean,
30270                 style_white : this.cwhite,
30271                 style_black : this.cblack
30272         });
30273         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30274         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30275          
30276         
30277     },
30278     
30279     /**
30280      * Clean up MS wordisms...
30281      * @deprecated - use filter directly
30282      */
30283     cleanWord : function(node)
30284     {
30285         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30286         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
30287         
30288     },
30289    
30290     
30291     /**
30292
30293      * @deprecated - use filters
30294      */
30295     cleanTableWidths : function(node)
30296     {
30297         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30298         
30299  
30300     },
30301     
30302      
30303         
30304     applyBlacklists : function()
30305     {
30306         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30307         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30308         
30309         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30310         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30311         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30312         
30313         this.white = [];
30314         this.black = [];
30315         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30316             if (b.indexOf(tag) > -1) {
30317                 return;
30318             }
30319             this.white.push(tag);
30320             
30321         }, this);
30322         
30323         Roo.each(w, function(tag) {
30324             if (b.indexOf(tag) > -1) {
30325                 return;
30326             }
30327             if (this.white.indexOf(tag) > -1) {
30328                 return;
30329             }
30330             this.white.push(tag);
30331             
30332         }, this);
30333         
30334         
30335         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30336             if (w.indexOf(tag) > -1) {
30337                 return;
30338             }
30339             this.black.push(tag);
30340             
30341         }, this);
30342         
30343         Roo.each(b, function(tag) {
30344             if (w.indexOf(tag) > -1) {
30345                 return;
30346             }
30347             if (this.black.indexOf(tag) > -1) {
30348                 return;
30349             }
30350             this.black.push(tag);
30351             
30352         }, this);
30353         
30354         
30355         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30356         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30357         
30358         this.cwhite = [];
30359         this.cblack = [];
30360         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30361             if (b.indexOf(tag) > -1) {
30362                 return;
30363             }
30364             this.cwhite.push(tag);
30365             
30366         }, this);
30367         
30368         Roo.each(w, function(tag) {
30369             if (b.indexOf(tag) > -1) {
30370                 return;
30371             }
30372             if (this.cwhite.indexOf(tag) > -1) {
30373                 return;
30374             }
30375             this.cwhite.push(tag);
30376             
30377         }, this);
30378         
30379         
30380         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30381             if (w.indexOf(tag) > -1) {
30382                 return;
30383             }
30384             this.cblack.push(tag);
30385             
30386         }, this);
30387         
30388         Roo.each(b, function(tag) {
30389             if (w.indexOf(tag) > -1) {
30390                 return;
30391             }
30392             if (this.cblack.indexOf(tag) > -1) {
30393                 return;
30394             }
30395             this.cblack.push(tag);
30396             
30397         }, this);
30398     },
30399     
30400     setStylesheets : function(stylesheets)
30401     {
30402         if(typeof(stylesheets) == 'string'){
30403             Roo.get(this.iframe.contentDocument.head).createChild({
30404                 tag : 'link',
30405                 rel : 'stylesheet',
30406                 type : 'text/css',
30407                 href : stylesheets
30408             });
30409             
30410             return;
30411         }
30412         var _this = this;
30413      
30414         Roo.each(stylesheets, function(s) {
30415             if(!s.length){
30416                 return;
30417             }
30418             
30419             Roo.get(_this.iframe.contentDocument.head).createChild({
30420                 tag : 'link',
30421                 rel : 'stylesheet',
30422                 type : 'text/css',
30423                 href : s
30424             });
30425         });
30426
30427         
30428     },
30429     
30430     
30431     updateLanguage : function()
30432     {
30433         if (!this.iframe || !this.iframe.contentDocument) {
30434             return;
30435         }
30436         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30437     },
30438     
30439     
30440     removeStylesheets : function()
30441     {
30442         var _this = this;
30443         
30444         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30445             s.remove();
30446         });
30447     },
30448     
30449     setStyle : function(style)
30450     {
30451         Roo.get(this.iframe.contentDocument.head).createChild({
30452             tag : 'style',
30453             type : 'text/css',
30454             html : style
30455         });
30456
30457         return;
30458     }
30459     
30460     // hide stuff that is not compatible
30461     /**
30462      * @event blur
30463      * @hide
30464      */
30465     /**
30466      * @event change
30467      * @hide
30468      */
30469     /**
30470      * @event focus
30471      * @hide
30472      */
30473     /**
30474      * @event specialkey
30475      * @hide
30476      */
30477     /**
30478      * @cfg {String} fieldClass @hide
30479      */
30480     /**
30481      * @cfg {String} focusClass @hide
30482      */
30483     /**
30484      * @cfg {String} autoCreate @hide
30485      */
30486     /**
30487      * @cfg {String} inputType @hide
30488      */
30489     /**
30490      * @cfg {String} invalidClass @hide
30491      */
30492     /**
30493      * @cfg {String} invalidText @hide
30494      */
30495     /**
30496      * @cfg {String} msgFx @hide
30497      */
30498     /**
30499      * @cfg {String} validateOnBlur @hide
30500      */
30501 });
30502
30503 Roo.HtmlEditorCore.white = [
30504         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30505         
30506        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30507        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30508        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30509        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30510        'TABLE',   'UL',         'XMP', 
30511        
30512        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30513       'THEAD',   'TR', 
30514      
30515       'DIR', 'MENU', 'OL', 'UL', 'DL',
30516        
30517       'EMBED',  'OBJECT'
30518 ];
30519
30520
30521 Roo.HtmlEditorCore.black = [
30522     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30523         'APPLET', // 
30524         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30525         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30526         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30527         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30528         //'FONT' // CLEAN LATER..
30529         'COLGROUP', 'COL'   // messy tables.
30530         
30531         
30532 ];
30533 Roo.HtmlEditorCore.clean = [ // ?? needed???
30534      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30535 ];
30536 Roo.HtmlEditorCore.tag_remove = [
30537     'FONT', 'TBODY'  
30538 ];
30539 // attributes..
30540
30541 Roo.HtmlEditorCore.ablack = [
30542     'on'
30543 ];
30544     
30545 Roo.HtmlEditorCore.aclean = [ 
30546     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30547 ];
30548
30549 // protocols..
30550 Roo.HtmlEditorCore.pwhite= [
30551         'http',  'https',  'mailto'
30552 ];
30553
30554 // white listed style attributes.
30555 Roo.HtmlEditorCore.cwhite= [
30556       //  'text-align', /// default is to allow most things..
30557       
30558          
30559 //        'font-size'//??
30560 ];
30561
30562 // black listed style attributes.
30563 Roo.HtmlEditorCore.cblack= [
30564       //  'font-size' -- this can be set by the project 
30565 ];
30566
30567
30568
30569
30570     /*
30571  * - LGPL
30572  *
30573  * HtmlEditor
30574  * 
30575  */
30576
30577 /**
30578  * @class Roo.bootstrap.form.HtmlEditor
30579  * @extends Roo.bootstrap.form.TextArea
30580  * Bootstrap HtmlEditor class
30581
30582  * @constructor
30583  * Create a new HtmlEditor
30584  * @param {Object} config The config object
30585  */
30586
30587 Roo.bootstrap.form.HtmlEditor = function(config){
30588     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30589     if (!this.toolbars) {
30590         this.toolbars = [];
30591     }
30592     
30593     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30594     this.addEvents({
30595             /**
30596              * @event initialize
30597              * Fires when the editor is fully initialized (including the iframe)
30598              * @param {HtmlEditor} this
30599              */
30600             initialize: true,
30601             /**
30602              * @event activate
30603              * Fires when the editor is first receives the focus. Any insertion must wait
30604              * until after this event.
30605              * @param {HtmlEditor} this
30606              */
30607             activate: true,
30608              /**
30609              * @event beforesync
30610              * Fires before the textarea is updated with content from the editor iframe. Return false
30611              * to cancel the sync.
30612              * @param {HtmlEditor} this
30613              * @param {String} html
30614              */
30615             beforesync: true,
30616              /**
30617              * @event beforepush
30618              * Fires before the iframe editor is updated with content from the textarea. Return false
30619              * to cancel the push.
30620              * @param {HtmlEditor} this
30621              * @param {String} html
30622              */
30623             beforepush: true,
30624              /**
30625              * @event sync
30626              * Fires when the textarea is updated with content from the editor iframe.
30627              * @param {HtmlEditor} this
30628              * @param {String} html
30629              */
30630             sync: true,
30631              /**
30632              * @event push
30633              * Fires when the iframe editor is updated with content from the textarea.
30634              * @param {HtmlEditor} this
30635              * @param {String} html
30636              */
30637             push: true,
30638              /**
30639              * @event editmodechange
30640              * Fires when the editor switches edit modes
30641              * @param {HtmlEditor} this
30642              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30643              */
30644             editmodechange: true,
30645             /**
30646              * @event editorevent
30647              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30648              * @param {HtmlEditor} this
30649              */
30650             editorevent: true,
30651             /**
30652              * @event firstfocus
30653              * Fires when on first focus - needed by toolbars..
30654              * @param {HtmlEditor} this
30655              */
30656             firstfocus: true,
30657             /**
30658              * @event autosave
30659              * Auto save the htmlEditor value as a file into Events
30660              * @param {HtmlEditor} this
30661              */
30662             autosave: true,
30663             /**
30664              * @event savedpreview
30665              * preview the saved version of htmlEditor
30666              * @param {HtmlEditor} this
30667              */
30668             savedpreview: true
30669         });
30670 };
30671
30672
30673 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30674     
30675     
30676       /**
30677      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30678      */
30679     toolbars : false,
30680     
30681      /**
30682     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30683     */
30684     btns : [],
30685    
30686      /**
30687      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30688      *                        Roo.resizable.
30689      */
30690     resizable : false,
30691      /**
30692      * @cfg {Number} height (in pixels)
30693      */   
30694     height: 300,
30695    /**
30696      * @cfg {Number} width (in pixels)
30697      */   
30698     width: false,
30699     
30700     /**
30701      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30702      * 
30703      */
30704     stylesheets: false,
30705     
30706     // id of frame..
30707     frameId: false,
30708     
30709     // private properties
30710     validationEvent : false,
30711     deferHeight: true,
30712     initialized : false,
30713     activated : false,
30714     
30715     onFocus : Roo.emptyFn,
30716     iframePad:3,
30717     hideMode:'offsets',
30718     
30719     tbContainer : false,
30720     
30721     bodyCls : '',
30722     
30723     toolbarContainer :function() {
30724         return this.wrap.select('.x-html-editor-tb',true).first();
30725     },
30726
30727     /**
30728      * Protected method that will not generally be called directly. It
30729      * is called when the editor creates its toolbar. Override this method if you need to
30730      * add custom toolbar buttons.
30731      * @param {HtmlEditor} editor
30732      */
30733     createToolbar : function(){
30734         Roo.log('renewing');
30735         Roo.log("create toolbars");
30736         
30737         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30738         this.toolbars[0].render(this.toolbarContainer());
30739         
30740         return;
30741         
30742 //        if (!editor.toolbars || !editor.toolbars.length) {
30743 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30744 //        }
30745 //        
30746 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30747 //            editor.toolbars[i] = Roo.factory(
30748 //                    typeof(editor.toolbars[i]) == 'string' ?
30749 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30750 //                Roo.bootstrap.form.HtmlEditor);
30751 //            editor.toolbars[i].init(editor);
30752 //        }
30753     },
30754
30755      
30756     // private
30757     onRender : function(ct, position)
30758     {
30759        // Roo.log("Call onRender: " + this.xtype);
30760         var _t = this;
30761         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30762       
30763         this.wrap = this.inputEl().wrap({
30764             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30765         });
30766         
30767         this.editorcore.onRender(ct, position);
30768          
30769         if (this.resizable) {
30770             this.resizeEl = new Roo.Resizable(this.wrap, {
30771                 pinned : true,
30772                 wrap: true,
30773                 dynamic : true,
30774                 minHeight : this.height,
30775                 height: this.height,
30776                 handles : this.resizable,
30777                 width: this.width,
30778                 listeners : {
30779                     resize : function(r, w, h) {
30780                         _t.onResize(w,h); // -something
30781                     }
30782                 }
30783             });
30784             
30785         }
30786         this.createToolbar(this);
30787        
30788         
30789         if(!this.width && this.resizable){
30790             this.setSize(this.wrap.getSize());
30791         }
30792         if (this.resizeEl) {
30793             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30794             // should trigger onReize..
30795         }
30796         
30797     },
30798
30799     // private
30800     onResize : function(w, h)
30801     {
30802         Roo.log('resize: ' +w + ',' + h );
30803         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30804         var ew = false;
30805         var eh = false;
30806         
30807         if(this.inputEl() ){
30808             if(typeof w == 'number'){
30809                 var aw = w - this.wrap.getFrameWidth('lr');
30810                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30811                 ew = aw;
30812             }
30813             if(typeof h == 'number'){
30814                  var tbh = -11;  // fixme it needs to tool bar size!
30815                 for (var i =0; i < this.toolbars.length;i++) {
30816                     // fixme - ask toolbars for heights?
30817                     tbh += this.toolbars[i].el.getHeight();
30818                     //if (this.toolbars[i].footer) {
30819                     //    tbh += this.toolbars[i].footer.el.getHeight();
30820                     //}
30821                 }
30822               
30823                 
30824                 
30825                 
30826                 
30827                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30828                 ah -= 5; // knock a few pixes off for look..
30829                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30830                 var eh = ah;
30831             }
30832         }
30833         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30834         this.editorcore.onResize(ew,eh);
30835         
30836     },
30837
30838     /**
30839      * Toggles the editor between standard and source edit mode.
30840      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30841      */
30842     toggleSourceEdit : function(sourceEditMode)
30843     {
30844         this.editorcore.toggleSourceEdit(sourceEditMode);
30845         
30846         if(this.editorcore.sourceEditMode){
30847             Roo.log('editor - showing textarea');
30848             
30849 //            Roo.log('in');
30850 //            Roo.log(this.syncValue());
30851             this.syncValue();
30852             this.inputEl().removeClass(['hide', 'x-hidden']);
30853             this.inputEl().dom.removeAttribute('tabIndex');
30854             this.inputEl().focus();
30855         }else{
30856             Roo.log('editor - hiding textarea');
30857 //            Roo.log('out')
30858 //            Roo.log(this.pushValue()); 
30859             this.pushValue();
30860             
30861             this.inputEl().addClass(['hide', 'x-hidden']);
30862             this.inputEl().dom.setAttribute('tabIndex', -1);
30863             //this.deferFocus();
30864         }
30865          
30866         if(this.resizable){
30867             this.setSize(this.wrap.getSize());
30868         }
30869         
30870         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30871     },
30872  
30873     // private (for BoxComponent)
30874     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30875
30876     // private (for BoxComponent)
30877     getResizeEl : function(){
30878         return this.wrap;
30879     },
30880
30881     // private (for BoxComponent)
30882     getPositionEl : function(){
30883         return this.wrap;
30884     },
30885
30886     // private
30887     initEvents : function(){
30888         this.originalValue = this.getValue();
30889     },
30890
30891 //    /**
30892 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30893 //     * @method
30894 //     */
30895 //    markInvalid : Roo.emptyFn,
30896 //    /**
30897 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30898 //     * @method
30899 //     */
30900 //    clearInvalid : Roo.emptyFn,
30901
30902     setValue : function(v){
30903         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30904         this.editorcore.pushValue();
30905     },
30906
30907      
30908     // private
30909     deferFocus : function(){
30910         this.focus.defer(10, this);
30911     },
30912
30913     // doc'ed in Field
30914     focus : function(){
30915         this.editorcore.focus();
30916         
30917     },
30918       
30919
30920     // private
30921     onDestroy : function(){
30922         
30923         
30924         
30925         if(this.rendered){
30926             
30927             for (var i =0; i < this.toolbars.length;i++) {
30928                 // fixme - ask toolbars for heights?
30929                 this.toolbars[i].onDestroy();
30930             }
30931             
30932             this.wrap.dom.innerHTML = '';
30933             this.wrap.remove();
30934         }
30935     },
30936
30937     // private
30938     onFirstFocus : function(){
30939         //Roo.log("onFirstFocus");
30940         this.editorcore.onFirstFocus();
30941          for (var i =0; i < this.toolbars.length;i++) {
30942             this.toolbars[i].onFirstFocus();
30943         }
30944         
30945     },
30946     
30947     // private
30948     syncValue : function()
30949     {   
30950         this.editorcore.syncValue();
30951     },
30952     
30953     pushValue : function()
30954     {   
30955         this.editorcore.pushValue();
30956     }
30957      
30958     
30959     // hide stuff that is not compatible
30960     /**
30961      * @event blur
30962      * @hide
30963      */
30964     /**
30965      * @event change
30966      * @hide
30967      */
30968     /**
30969      * @event focus
30970      * @hide
30971      */
30972     /**
30973      * @event specialkey
30974      * @hide
30975      */
30976     /**
30977      * @cfg {String} fieldClass @hide
30978      */
30979     /**
30980      * @cfg {String} focusClass @hide
30981      */
30982     /**
30983      * @cfg {String} autoCreate @hide
30984      */
30985     /**
30986      * @cfg {String} inputType @hide
30987      */
30988      
30989     /**
30990      * @cfg {String} invalidText @hide
30991      */
30992     /**
30993      * @cfg {String} msgFx @hide
30994      */
30995     /**
30996      * @cfg {String} validateOnBlur @hide
30997      */
30998 });
30999  
31000     
31001    
31002    
31003    
31004       
31005 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
31006 /**
31007  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
31008  * @parent Roo.bootstrap.form.HtmlEditor
31009  * @extends Roo.bootstrap.nav.Simplebar
31010  * Basic Toolbar
31011  * 
31012  * @example
31013  * Usage:
31014  *
31015  new Roo.bootstrap.form.HtmlEditor({
31016     ....
31017     toolbars : [
31018         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
31019             disable : { fonts: 1 , format: 1, ..., ... , ...],
31020             btns : [ .... ]
31021         })
31022     }
31023      
31024  * 
31025  * @cfg {Object} disable List of elements to disable..
31026  * @cfg {Array} btns List of additional buttons.
31027  * 
31028  * 
31029  * NEEDS Extra CSS? 
31030  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
31031  */
31032  
31033 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
31034 {
31035     
31036     Roo.apply(this, config);
31037     
31038     // default disabled, based on 'good practice'..
31039     this.disable = this.disable || {};
31040     Roo.applyIf(this.disable, {
31041         fontSize : true,
31042         colors : true,
31043         specialElements : true
31044     });
31045     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
31046     
31047     this.editor = config.editor;
31048     this.editorcore = config.editor.editorcore;
31049     
31050     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
31051     
31052     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
31053     // dont call parent... till later.
31054 }
31055 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
31056      
31057     bar : true,
31058     
31059     editor : false,
31060     editorcore : false,
31061     
31062     
31063     formats : [
31064         "p" ,  
31065         "h1","h2","h3","h4","h5","h6", 
31066         "pre", "code", 
31067         "abbr", "acronym", "address", "cite", "samp", "var",
31068         'div','span'
31069     ],
31070     
31071     onRender : function(ct, position)
31072     {
31073        // Roo.log("Call onRender: " + this.xtype);
31074         
31075        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
31076        Roo.log(this.el);
31077        this.el.dom.style.marginBottom = '0';
31078        var _this = this;
31079        var editorcore = this.editorcore;
31080        var editor= this.editor;
31081        
31082        var children = [];
31083        var btn = function(id,cmd , toggle, handler, html){
31084        
31085             var  event = toggle ? 'toggle' : 'click';
31086        
31087             var a = {
31088                 size : 'sm',
31089                 xtype: 'Button',
31090                 xns: Roo.bootstrap,
31091                 //glyphicon : id,
31092                 fa: id,
31093                 cmd : id || cmd,
31094                 enableToggle:toggle !== false,
31095                 html : html || '',
31096                 pressed : toggle ? false : null,
31097                 listeners : {}
31098             };
31099             a.listeners[toggle ? 'toggle' : 'click'] = function() {
31100                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
31101             };
31102             children.push(a);
31103             return a;
31104        }
31105        
31106     //    var cb_box = function...
31107         
31108         var style = {
31109                 xtype: 'Button',
31110                 size : 'sm',
31111                 xns: Roo.bootstrap,
31112                 fa : 'font',
31113                 //html : 'submit'
31114                 menu : {
31115                     xtype: 'Menu',
31116                     xns: Roo.bootstrap,
31117                     items:  []
31118                 }
31119         };
31120         Roo.each(this.formats, function(f) {
31121             style.menu.items.push({
31122                 xtype :'MenuItem',
31123                 xns: Roo.bootstrap,
31124                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
31125                 tagname : f,
31126                 listeners : {
31127                     click : function()
31128                     {
31129                         editorcore.insertTag(this.tagname);
31130                         editor.focus();
31131                     }
31132                 }
31133                 
31134             });
31135         });
31136         children.push(style);   
31137         
31138         btn('bold',false,true);
31139         btn('italic',false,true);
31140         btn('align-left', 'justifyleft',true);
31141         btn('align-center', 'justifycenter',true);
31142         btn('align-right' , 'justifyright',true);
31143         btn('link', false, false, function(btn) {
31144             //Roo.log("create link?");
31145             var url = prompt(this.createLinkText, this.defaultLinkValue);
31146             if(url && url != 'http:/'+'/'){
31147                 this.editorcore.relayCmd('createlink', url);
31148             }
31149         }),
31150         btn('list','insertunorderedlist',true);
31151         btn('pencil', false,true, function(btn){
31152                 Roo.log(this);
31153                 this.toggleSourceEdit(btn.pressed);
31154         });
31155         
31156         if (this.editor.btns.length > 0) {
31157             for (var i = 0; i<this.editor.btns.length; i++) {
31158                 children.push(this.editor.btns[i]);
31159             }
31160         }
31161         
31162         /*
31163         var cog = {
31164                 xtype: 'Button',
31165                 size : 'sm',
31166                 xns: Roo.bootstrap,
31167                 glyphicon : 'cog',
31168                 //html : 'submit'
31169                 menu : {
31170                     xtype: 'Menu',
31171                     xns: Roo.bootstrap,
31172                     items:  []
31173                 }
31174         };
31175         
31176         cog.menu.items.push({
31177             xtype :'MenuItem',
31178             xns: Roo.bootstrap,
31179             html : Clean styles,
31180             tagname : f,
31181             listeners : {
31182                 click : function()
31183                 {
31184                     editorcore.insertTag(this.tagname);
31185                     editor.focus();
31186                 }
31187             }
31188             
31189         });
31190        */
31191         
31192          
31193        this.xtype = 'NavSimplebar';
31194         
31195         for(var i=0;i< children.length;i++) {
31196             
31197             this.buttons.add(this.addxtypeChild(children[i]));
31198             
31199         }
31200         
31201         editor.on('editorevent', this.updateToolbar, this);
31202     },
31203     onBtnClick : function(id)
31204     {
31205        this.editorcore.relayCmd(id);
31206        this.editorcore.focus();
31207     },
31208     
31209     /**
31210      * Protected method that will not generally be called directly. It triggers
31211      * a toolbar update by reading the markup state of the current selection in the editor.
31212      */
31213     updateToolbar: function(){
31214
31215         if(!this.editorcore.activated){
31216             this.editor.onFirstFocus(); // is this neeed?
31217             return;
31218         }
31219
31220         var btns = this.buttons; 
31221         var doc = this.editorcore.doc;
31222         btns.get('bold').setActive(doc.queryCommandState('bold'));
31223         btns.get('italic').setActive(doc.queryCommandState('italic'));
31224         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31225         
31226         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31227         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31228         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31229         
31230         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31231         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31232          /*
31233         
31234         var ans = this.editorcore.getAllAncestors();
31235         if (this.formatCombo) {
31236             
31237             
31238             var store = this.formatCombo.store;
31239             this.formatCombo.setValue("");
31240             for (var i =0; i < ans.length;i++) {
31241                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31242                     // select it..
31243                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31244                     break;
31245                 }
31246             }
31247         }
31248         
31249         
31250         
31251         // hides menus... - so this cant be on a menu...
31252         Roo.bootstrap.MenuMgr.hideAll();
31253         */
31254         Roo.bootstrap.menu.Manager.hideAll();
31255         //this.editorsyncValue();
31256     },
31257     onFirstFocus: function() {
31258         this.buttons.each(function(item){
31259            item.enable();
31260         });
31261     },
31262     toggleSourceEdit : function(sourceEditMode){
31263         
31264           
31265         if(sourceEditMode){
31266             Roo.log("disabling buttons");
31267            this.buttons.each( function(item){
31268                 if(item.cmd != 'pencil'){
31269                     item.disable();
31270                 }
31271             });
31272           
31273         }else{
31274             Roo.log("enabling buttons");
31275             if(this.editorcore.initialized){
31276                 this.buttons.each( function(item){
31277                     item.enable();
31278                 });
31279             }
31280             
31281         }
31282         Roo.log("calling toggole on editor");
31283         // tell the editor that it's been pressed..
31284         this.editor.toggleSourceEdit(sourceEditMode);
31285        
31286     }
31287 });
31288
31289
31290
31291
31292  
31293 /*
31294  * - LGPL
31295  */
31296
31297 /**
31298  * @class Roo.bootstrap.form.Markdown
31299  * @extends Roo.bootstrap.form.TextArea
31300  * Bootstrap Showdown editable area
31301  * @cfg {string} content
31302  * 
31303  * @constructor
31304  * Create a new Showdown
31305  */
31306
31307 Roo.bootstrap.form.Markdown = function(config){
31308     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31309    
31310 };
31311
31312 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31313     
31314     editing :false,
31315     
31316     initEvents : function()
31317     {
31318         
31319         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31320         this.markdownEl = this.el.createChild({
31321             cls : 'roo-markdown-area'
31322         });
31323         this.inputEl().addClass('d-none');
31324         if (this.getValue() == '') {
31325             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31326             
31327         } else {
31328             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31329         }
31330         this.markdownEl.on('click', this.toggleTextEdit, this);
31331         this.on('blur', this.toggleTextEdit, this);
31332         this.on('specialkey', this.resizeTextArea, this);
31333     },
31334     
31335     toggleTextEdit : function()
31336     {
31337         var sh = this.markdownEl.getHeight();
31338         this.inputEl().addClass('d-none');
31339         this.markdownEl.addClass('d-none');
31340         if (!this.editing) {
31341             // show editor?
31342             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31343             this.inputEl().removeClass('d-none');
31344             this.inputEl().focus();
31345             this.editing = true;
31346             return;
31347         }
31348         // show showdown...
31349         this.updateMarkdown();
31350         this.markdownEl.removeClass('d-none');
31351         this.editing = false;
31352         return;
31353     },
31354     updateMarkdown : function()
31355     {
31356         if (this.getValue() == '') {
31357             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31358             return;
31359         }
31360  
31361         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31362     },
31363     
31364     resizeTextArea: function () {
31365         
31366         var sh = 100;
31367         Roo.log([sh, this.getValue().split("\n").length * 30]);
31368         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31369     },
31370     setValue : function(val)
31371     {
31372         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31373         if (!this.editing) {
31374             this.updateMarkdown();
31375         }
31376         
31377     },
31378     focus : function()
31379     {
31380         if (!this.editing) {
31381             this.toggleTextEdit();
31382         }
31383         
31384     }
31385
31386
31387 });/*
31388  * Based on:
31389  * Ext JS Library 1.1.1
31390  * Copyright(c) 2006-2007, Ext JS, LLC.
31391  *
31392  * Originally Released Under LGPL - original licence link has changed is not relivant.
31393  *
31394  * Fork - LGPL
31395  * <script type="text/javascript">
31396  */
31397  
31398 /**
31399  * @class Roo.bootstrap.PagingToolbar
31400  * @extends Roo.bootstrap.nav.Simplebar
31401  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31402  * @constructor
31403  * Create a new PagingToolbar
31404  * @param {Object} config The config object
31405  * @param {Roo.data.Store} store
31406  */
31407 Roo.bootstrap.PagingToolbar = function(config)
31408 {
31409     // old args format still supported... - xtype is prefered..
31410         // created from xtype...
31411     
31412     this.ds = config.dataSource;
31413     
31414     if (config.store && !this.ds) {
31415         this.store= Roo.factory(config.store, Roo.data);
31416         this.ds = this.store;
31417         this.ds.xmodule = this.xmodule || false;
31418     }
31419     
31420     this.toolbarItems = [];
31421     if (config.items) {
31422         this.toolbarItems = config.items;
31423     }
31424     
31425     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31426     
31427     this.cursor = 0;
31428     
31429     if (this.ds) { 
31430         this.bind(this.ds);
31431     }
31432     
31433     if (Roo.bootstrap.version == 4) {
31434         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31435     } else {
31436         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31437     }
31438     
31439 };
31440
31441 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31442     /**
31443      * @cfg {Roo.bootstrap.Button} buttons[]
31444      * Buttons for the toolbar
31445      */
31446      /**
31447      * @cfg {Roo.data.Store} store
31448      * The underlying data store providing the paged data
31449      */
31450     /**
31451      * @cfg {String/HTMLElement/Element} container
31452      * container The id or element that will contain the toolbar
31453      */
31454     /**
31455      * @cfg {Boolean} displayInfo
31456      * True to display the displayMsg (defaults to false)
31457      */
31458     /**
31459      * @cfg {Number} pageSize
31460      * The number of records to display per page (defaults to 20)
31461      */
31462     pageSize: 20,
31463     /**
31464      * @cfg {String} displayMsg
31465      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31466      */
31467     displayMsg : 'Displaying {0} - {1} of {2}',
31468     /**
31469      * @cfg {String} emptyMsg
31470      * The message to display when no records are found (defaults to "No data to display")
31471      */
31472     emptyMsg : 'No data to display',
31473     /**
31474      * Customizable piece of the default paging text (defaults to "Page")
31475      * @type String
31476      */
31477     beforePageText : "Page",
31478     /**
31479      * Customizable piece of the default paging text (defaults to "of %0")
31480      * @type String
31481      */
31482     afterPageText : "of {0}",
31483     /**
31484      * Customizable piece of the default paging text (defaults to "First Page")
31485      * @type String
31486      */
31487     firstText : "First Page",
31488     /**
31489      * Customizable piece of the default paging text (defaults to "Previous Page")
31490      * @type String
31491      */
31492     prevText : "Previous Page",
31493     /**
31494      * Customizable piece of the default paging text (defaults to "Next Page")
31495      * @type String
31496      */
31497     nextText : "Next Page",
31498     /**
31499      * Customizable piece of the default paging text (defaults to "Last Page")
31500      * @type String
31501      */
31502     lastText : "Last Page",
31503     /**
31504      * Customizable piece of the default paging text (defaults to "Refresh")
31505      * @type String
31506      */
31507     refreshText : "Refresh",
31508
31509     buttons : false,
31510     // private
31511     onRender : function(ct, position) 
31512     {
31513         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31514         this.navgroup.parentId = this.id;
31515         this.navgroup.onRender(this.el, null);
31516         // add the buttons to the navgroup
31517         
31518         if(this.displayInfo){
31519             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31520             this.displayEl = this.el.select('.x-paging-info', true).first();
31521 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31522 //            this.displayEl = navel.el.select('span',true).first();
31523         }
31524         
31525         var _this = this;
31526         
31527         if(this.buttons){
31528             Roo.each(_this.buttons, function(e){ // this might need to use render????
31529                Roo.factory(e).render(_this.el);
31530             });
31531         }
31532             
31533         Roo.each(_this.toolbarItems, function(e) {
31534             _this.navgroup.addItem(e);
31535         });
31536         
31537         
31538         this.first = this.navgroup.addItem({
31539             tooltip: this.firstText,
31540             cls: "prev btn-outline-secondary",
31541             html : ' <i class="fa fa-step-backward"></i>',
31542             disabled: true,
31543             preventDefault: true,
31544             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31545         });
31546         
31547         this.prev =  this.navgroup.addItem({
31548             tooltip: this.prevText,
31549             cls: "prev btn-outline-secondary",
31550             html : ' <i class="fa fa-backward"></i>',
31551             disabled: true,
31552             preventDefault: true,
31553             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31554         });
31555     //this.addSeparator();
31556         
31557         
31558         var field = this.navgroup.addItem( {
31559             tagtype : 'span',
31560             cls : 'x-paging-position  btn-outline-secondary',
31561              disabled: true,
31562             html : this.beforePageText  +
31563                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31564                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31565          } ); //?? escaped?
31566         
31567         this.field = field.el.select('input', true).first();
31568         this.field.on("keydown", this.onPagingKeydown, this);
31569         this.field.on("focus", function(){this.dom.select();});
31570     
31571     
31572         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31573         //this.field.setHeight(18);
31574         //this.addSeparator();
31575         this.next = this.navgroup.addItem({
31576             tooltip: this.nextText,
31577             cls: "next btn-outline-secondary",
31578             html : ' <i class="fa fa-forward"></i>',
31579             disabled: true,
31580             preventDefault: true,
31581             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31582         });
31583         this.last = this.navgroup.addItem({
31584             tooltip: this.lastText,
31585             html : ' <i class="fa fa-step-forward"></i>',
31586             cls: "next btn-outline-secondary",
31587             disabled: true,
31588             preventDefault: true,
31589             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31590         });
31591     //this.addSeparator();
31592         this.loading = this.navgroup.addItem({
31593             tooltip: this.refreshText,
31594             cls: "btn-outline-secondary",
31595             html : ' <i class="fa fa-refresh"></i>',
31596             preventDefault: true,
31597             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31598         });
31599         
31600     },
31601
31602     // private
31603     updateInfo : function(){
31604         if(this.displayEl){
31605             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31606             var msg = count == 0 ?
31607                 this.emptyMsg :
31608                 String.format(
31609                     this.displayMsg,
31610                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31611                 );
31612             this.displayEl.update(msg);
31613         }
31614     },
31615
31616     // private
31617     onLoad : function(ds, r, o)
31618     {
31619         this.cursor = o.params && o.params.start ? o.params.start : 0;
31620         
31621         var d = this.getPageData(),
31622             ap = d.activePage,
31623             ps = d.pages;
31624         
31625         
31626         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31627         this.field.dom.value = ap;
31628         this.first.setDisabled(ap == 1);
31629         this.prev.setDisabled(ap == 1);
31630         this.next.setDisabled(ap == ps);
31631         this.last.setDisabled(ap == ps);
31632         this.loading.enable();
31633         this.updateInfo();
31634     },
31635
31636     // private
31637     getPageData : function(){
31638         var total = this.ds.getTotalCount();
31639         return {
31640             total : total,
31641             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31642             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31643         };
31644     },
31645
31646     // private
31647     onLoadError : function(proxy, o){
31648         this.loading.enable();
31649         if (this.ds.events.loadexception.listeners.length  < 2) {
31650             // nothing has been assigned to loadexception except this...
31651             // so 
31652             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31653
31654         }
31655     },
31656
31657     // private
31658     onPagingKeydown : function(e){
31659         var k = e.getKey();
31660         var d = this.getPageData();
31661         if(k == e.RETURN){
31662             var v = this.field.dom.value, pageNum;
31663             if(!v || isNaN(pageNum = parseInt(v, 10))){
31664                 this.field.dom.value = d.activePage;
31665                 return;
31666             }
31667             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31668             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31669             e.stopEvent();
31670         }
31671         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))
31672         {
31673           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31674           this.field.dom.value = pageNum;
31675           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31676           e.stopEvent();
31677         }
31678         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31679         {
31680           var v = this.field.dom.value, pageNum; 
31681           var increment = (e.shiftKey) ? 10 : 1;
31682           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31683                 increment *= -1;
31684           }
31685           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31686             this.field.dom.value = d.activePage;
31687             return;
31688           }
31689           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31690           {
31691             this.field.dom.value = parseInt(v, 10) + increment;
31692             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31693             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31694           }
31695           e.stopEvent();
31696         }
31697     },
31698
31699     // private
31700     beforeLoad : function(){
31701         if(this.loading){
31702             this.loading.disable();
31703         }
31704     },
31705
31706     // private
31707     onClick : function(which){
31708         
31709         var ds = this.ds;
31710         if (!ds) {
31711             return;
31712         }
31713         
31714         switch(which){
31715             case "first":
31716                 ds.load({params:{start: 0, limit: this.pageSize}});
31717             break;
31718             case "prev":
31719                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31720             break;
31721             case "next":
31722                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31723             break;
31724             case "last":
31725                 var total = ds.getTotalCount();
31726                 var extra = total % this.pageSize;
31727                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31728                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31729             break;
31730             case "refresh":
31731                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31732             break;
31733         }
31734     },
31735
31736     /**
31737      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31738      * @param {Roo.data.Store} store The data store to unbind
31739      */
31740     unbind : function(ds){
31741         ds.un("beforeload", this.beforeLoad, this);
31742         ds.un("load", this.onLoad, this);
31743         ds.un("loadexception", this.onLoadError, this);
31744         ds.un("remove", this.updateInfo, this);
31745         ds.un("add", this.updateInfo, this);
31746         this.ds = undefined;
31747     },
31748
31749     /**
31750      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31751      * @param {Roo.data.Store} store The data store to bind
31752      */
31753     bind : function(ds){
31754         ds.on("beforeload", this.beforeLoad, this);
31755         ds.on("load", this.onLoad, this);
31756         ds.on("loadexception", this.onLoadError, this);
31757         ds.on("remove", this.updateInfo, this);
31758         ds.on("add", this.updateInfo, this);
31759         this.ds = ds;
31760     }
31761 });/*
31762  * - LGPL
31763  *
31764  * element
31765  * 
31766  */
31767
31768 /**
31769  * @class Roo.bootstrap.MessageBar
31770  * @extends Roo.bootstrap.Component
31771  * Bootstrap MessageBar class
31772  * @cfg {String} html contents of the MessageBar
31773  * @cfg {String} weight (info | success | warning | danger) default info
31774  * @cfg {String} beforeClass insert the bar before the given class
31775  * @cfg {Boolean} closable (true | false) default false
31776  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31777  * 
31778  * @constructor
31779  * Create a new Element
31780  * @param {Object} config The config object
31781  */
31782
31783 Roo.bootstrap.MessageBar = function(config){
31784     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31785 };
31786
31787 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31788     
31789     html: '',
31790     weight: 'info',
31791     closable: false,
31792     fixed: false,
31793     beforeClass: 'bootstrap-sticky-wrap',
31794     
31795     getAutoCreate : function(){
31796         
31797         var cfg = {
31798             tag: 'div',
31799             cls: 'alert alert-dismissable alert-' + this.weight,
31800             cn: [
31801                 {
31802                     tag: 'span',
31803                     cls: 'message',
31804                     html: this.html || ''
31805                 }
31806             ]
31807         };
31808         
31809         if(this.fixed){
31810             cfg.cls += ' alert-messages-fixed';
31811         }
31812         
31813         if(this.closable){
31814             cfg.cn.push({
31815                 tag: 'button',
31816                 cls: 'close',
31817                 html: 'x'
31818             });
31819         }
31820         
31821         return cfg;
31822     },
31823     
31824     onRender : function(ct, position)
31825     {
31826         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31827         
31828         if(!this.el){
31829             var cfg = Roo.apply({},  this.getAutoCreate());
31830             cfg.id = Roo.id();
31831             
31832             if (this.cls) {
31833                 cfg.cls += ' ' + this.cls;
31834             }
31835             if (this.style) {
31836                 cfg.style = this.style;
31837             }
31838             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31839             
31840             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31841         }
31842         
31843         this.el.select('>button.close').on('click', this.hide, this);
31844         
31845     },
31846     
31847     show : function()
31848     {
31849         if (!this.rendered) {
31850             this.render();
31851         }
31852         
31853         this.el.show();
31854         
31855         this.fireEvent('show', this);
31856         
31857     },
31858     
31859     hide : function()
31860     {
31861         if (!this.rendered) {
31862             this.render();
31863         }
31864         
31865         this.el.hide();
31866         
31867         this.fireEvent('hide', this);
31868     },
31869     
31870     update : function()
31871     {
31872 //        var e = this.el.dom.firstChild;
31873 //        
31874 //        if(this.closable){
31875 //            e = e.nextSibling;
31876 //        }
31877 //        
31878 //        e.data = this.html || '';
31879
31880         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31881     }
31882    
31883 });
31884
31885  
31886
31887      /*
31888  * - LGPL
31889  *
31890  * Graph
31891  * 
31892  */
31893
31894
31895 /**
31896  * @class Roo.bootstrap.Graph
31897  * @extends Roo.bootstrap.Component
31898  * Bootstrap Graph class
31899 > Prameters
31900  -sm {number} sm 4
31901  -md {number} md 5
31902  @cfg {String} graphtype  bar | vbar | pie
31903  @cfg {number} g_x coodinator | centre x (pie)
31904  @cfg {number} g_y coodinator | centre y (pie)
31905  @cfg {number} g_r radius (pie)
31906  @cfg {number} g_height height of the chart (respected by all elements in the set)
31907  @cfg {number} g_width width of the chart (respected by all elements in the set)
31908  @cfg {Object} title The title of the chart
31909     
31910  -{Array}  values
31911  -opts (object) options for the chart 
31912      o {
31913      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31914      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31915      o vgutter (number)
31916      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.
31917      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31918      o to
31919      o stretch (boolean)
31920      o }
31921  -opts (object) options for the pie
31922      o{
31923      o cut
31924      o startAngle (number)
31925      o endAngle (number)
31926      } 
31927  *
31928  * @constructor
31929  * Create a new Input
31930  * @param {Object} config The config object
31931  */
31932
31933 Roo.bootstrap.Graph = function(config){
31934     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31935     
31936     this.addEvents({
31937         // img events
31938         /**
31939          * @event click
31940          * The img click event for the img.
31941          * @param {Roo.EventObject} e
31942          */
31943         "click" : true
31944     });
31945 };
31946
31947 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31948     
31949     sm: 4,
31950     md: 5,
31951     graphtype: 'bar',
31952     g_height: 250,
31953     g_width: 400,
31954     g_x: 50,
31955     g_y: 50,
31956     g_r: 30,
31957     opts:{
31958         //g_colors: this.colors,
31959         g_type: 'soft',
31960         g_gutter: '20%'
31961
31962     },
31963     title : false,
31964
31965     getAutoCreate : function(){
31966         
31967         var cfg = {
31968             tag: 'div',
31969             html : null
31970         };
31971         
31972         
31973         return  cfg;
31974     },
31975
31976     onRender : function(ct,position){
31977         
31978         
31979         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31980         
31981         if (typeof(Raphael) == 'undefined') {
31982             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31983             return;
31984         }
31985         
31986         this.raphael = Raphael(this.el.dom);
31987         
31988                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31989                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31990                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31991                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31992                 /*
31993                 r.text(160, 10, "Single Series Chart").attr(txtattr);
31994                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31995                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31996                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31997                 
31998                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
31999                 r.barchart(330, 10, 300, 220, data1);
32000                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
32001                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
32002                 */
32003                 
32004                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32005                 // r.barchart(30, 30, 560, 250,  xdata, {
32006                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
32007                 //     axis : "0 0 1 1",
32008                 //     axisxlabels :  xdata
32009                 //     //yvalues : cols,
32010                    
32011                 // });
32012 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32013 //        
32014 //        this.load(null,xdata,{
32015 //                axis : "0 0 1 1",
32016 //                axisxlabels :  xdata
32017 //                });
32018
32019     },
32020
32021     load : function(graphtype,xdata,opts)
32022     {
32023         this.raphael.clear();
32024         if(!graphtype) {
32025             graphtype = this.graphtype;
32026         }
32027         if(!opts){
32028             opts = this.opts;
32029         }
32030         var r = this.raphael,
32031             fin = function () {
32032                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
32033             },
32034             fout = function () {
32035                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
32036             },
32037             pfin = function() {
32038                 this.sector.stop();
32039                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
32040
32041                 if (this.label) {
32042                     this.label[0].stop();
32043                     this.label[0].attr({ r: 7.5 });
32044                     this.label[1].attr({ "font-weight": 800 });
32045                 }
32046             },
32047             pfout = function() {
32048                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
32049
32050                 if (this.label) {
32051                     this.label[0].animate({ r: 5 }, 500, "bounce");
32052                     this.label[1].attr({ "font-weight": 400 });
32053                 }
32054             };
32055
32056         switch(graphtype){
32057             case 'bar':
32058                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32059                 break;
32060             case 'hbar':
32061                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32062                 break;
32063             case 'pie':
32064 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
32065 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
32066 //            
32067                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
32068                 
32069                 break;
32070
32071         }
32072         
32073         if(this.title){
32074             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
32075         }
32076         
32077     },
32078     
32079     setTitle: function(o)
32080     {
32081         this.title = o;
32082     },
32083     
32084     initEvents: function() {
32085         
32086         if(!this.href){
32087             this.el.on('click', this.onClick, this);
32088         }
32089     },
32090     
32091     onClick : function(e)
32092     {
32093         Roo.log('img onclick');
32094         this.fireEvent('click', this, e);
32095     }
32096    
32097 });
32098
32099  
32100 Roo.bootstrap.dash = {};/*
32101  * - LGPL
32102  *
32103  * numberBox
32104  * 
32105  */
32106 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32107
32108 /**
32109  * @class Roo.bootstrap.dash.NumberBox
32110  * @extends Roo.bootstrap.Component
32111  * Bootstrap NumberBox class
32112  * @cfg {String} headline Box headline
32113  * @cfg {String} content Box content
32114  * @cfg {String} icon Box icon
32115  * @cfg {String} footer Footer text
32116  * @cfg {String} fhref Footer href
32117  * 
32118  * @constructor
32119  * Create a new NumberBox
32120  * @param {Object} config The config object
32121  */
32122
32123
32124 Roo.bootstrap.dash.NumberBox = function(config){
32125     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
32126     
32127 };
32128
32129 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
32130     
32131     headline : '',
32132     content : '',
32133     icon : '',
32134     footer : '',
32135     fhref : '',
32136     ficon : '',
32137     
32138     getAutoCreate : function(){
32139         
32140         var cfg = {
32141             tag : 'div',
32142             cls : 'small-box ',
32143             cn : [
32144                 {
32145                     tag : 'div',
32146                     cls : 'inner',
32147                     cn :[
32148                         {
32149                             tag : 'h3',
32150                             cls : 'roo-headline',
32151                             html : this.headline
32152                         },
32153                         {
32154                             tag : 'p',
32155                             cls : 'roo-content',
32156                             html : this.content
32157                         }
32158                     ]
32159                 }
32160             ]
32161         };
32162         
32163         if(this.icon){
32164             cfg.cn.push({
32165                 tag : 'div',
32166                 cls : 'icon',
32167                 cn :[
32168                     {
32169                         tag : 'i',
32170                         cls : 'ion ' + this.icon
32171                     }
32172                 ]
32173             });
32174         }
32175         
32176         if(this.footer){
32177             var footer = {
32178                 tag : 'a',
32179                 cls : 'small-box-footer',
32180                 href : this.fhref || '#',
32181                 html : this.footer
32182             };
32183             
32184             cfg.cn.push(footer);
32185             
32186         }
32187         
32188         return  cfg;
32189     },
32190
32191     onRender : function(ct,position){
32192         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32193
32194
32195        
32196                 
32197     },
32198
32199     setHeadline: function (value)
32200     {
32201         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32202     },
32203     
32204     setFooter: function (value, href)
32205     {
32206         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32207         
32208         if(href){
32209             this.el.select('a.small-box-footer',true).first().attr('href', href);
32210         }
32211         
32212     },
32213
32214     setContent: function (value)
32215     {
32216         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32217     },
32218
32219     initEvents: function() 
32220     {   
32221         
32222     }
32223     
32224 });
32225
32226  
32227 /*
32228  * - LGPL
32229  *
32230  * TabBox
32231  * 
32232  */
32233 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32234
32235 /**
32236  * @class Roo.bootstrap.dash.TabBox
32237  * @extends Roo.bootstrap.Component
32238  * @children Roo.bootstrap.dash.TabPane
32239  * Bootstrap TabBox class
32240  * @cfg {String} title Title of the TabBox
32241  * @cfg {String} icon Icon of the TabBox
32242  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32243  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32244  * 
32245  * @constructor
32246  * Create a new TabBox
32247  * @param {Object} config The config object
32248  */
32249
32250
32251 Roo.bootstrap.dash.TabBox = function(config){
32252     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32253     this.addEvents({
32254         // raw events
32255         /**
32256          * @event addpane
32257          * When a pane is added
32258          * @param {Roo.bootstrap.dash.TabPane} pane
32259          */
32260         "addpane" : true,
32261         /**
32262          * @event activatepane
32263          * When a pane is activated
32264          * @param {Roo.bootstrap.dash.TabPane} pane
32265          */
32266         "activatepane" : true
32267         
32268          
32269     });
32270     
32271     this.panes = [];
32272 };
32273
32274 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32275
32276     title : '',
32277     icon : false,
32278     showtabs : true,
32279     tabScrollable : false,
32280     
32281     getChildContainer : function()
32282     {
32283         return this.el.select('.tab-content', true).first();
32284     },
32285     
32286     getAutoCreate : function(){
32287         
32288         var header = {
32289             tag: 'li',
32290             cls: 'pull-left header',
32291             html: this.title,
32292             cn : []
32293         };
32294         
32295         if(this.icon){
32296             header.cn.push({
32297                 tag: 'i',
32298                 cls: 'fa ' + this.icon
32299             });
32300         }
32301         
32302         var h = {
32303             tag: 'ul',
32304             cls: 'nav nav-tabs pull-right',
32305             cn: [
32306                 header
32307             ]
32308         };
32309         
32310         if(this.tabScrollable){
32311             h = {
32312                 tag: 'div',
32313                 cls: 'tab-header',
32314                 cn: [
32315                     {
32316                         tag: 'ul',
32317                         cls: 'nav nav-tabs pull-right',
32318                         cn: [
32319                             header
32320                         ]
32321                     }
32322                 ]
32323             };
32324         }
32325         
32326         var cfg = {
32327             tag: 'div',
32328             cls: 'nav-tabs-custom',
32329             cn: [
32330                 h,
32331                 {
32332                     tag: 'div',
32333                     cls: 'tab-content no-padding',
32334                     cn: []
32335                 }
32336             ]
32337         };
32338
32339         return  cfg;
32340     },
32341     initEvents : function()
32342     {
32343         //Roo.log('add add pane handler');
32344         this.on('addpane', this.onAddPane, this);
32345     },
32346      /**
32347      * Updates the box title
32348      * @param {String} html to set the title to.
32349      */
32350     setTitle : function(value)
32351     {
32352         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32353     },
32354     onAddPane : function(pane)
32355     {
32356         this.panes.push(pane);
32357         //Roo.log('addpane');
32358         //Roo.log(pane);
32359         // tabs are rendere left to right..
32360         if(!this.showtabs){
32361             return;
32362         }
32363         
32364         var ctr = this.el.select('.nav-tabs', true).first();
32365          
32366          
32367         var existing = ctr.select('.nav-tab',true);
32368         var qty = existing.getCount();;
32369         
32370         
32371         var tab = ctr.createChild({
32372             tag : 'li',
32373             cls : 'nav-tab' + (qty ? '' : ' active'),
32374             cn : [
32375                 {
32376                     tag : 'a',
32377                     href:'#',
32378                     html : pane.title
32379                 }
32380             ]
32381         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32382         pane.tab = tab;
32383         
32384         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32385         if (!qty) {
32386             pane.el.addClass('active');
32387         }
32388         
32389                 
32390     },
32391     onTabClick : function(ev,un,ob,pane)
32392     {
32393         //Roo.log('tab - prev default');
32394         ev.preventDefault();
32395         
32396         
32397         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32398         pane.tab.addClass('active');
32399         //Roo.log(pane.title);
32400         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32401         // technically we should have a deactivate event.. but maybe add later.
32402         // and it should not de-activate the selected tab...
32403         this.fireEvent('activatepane', pane);
32404         pane.el.addClass('active');
32405         pane.fireEvent('activate');
32406         
32407         
32408     },
32409     
32410     getActivePane : function()
32411     {
32412         var r = false;
32413         Roo.each(this.panes, function(p) {
32414             if(p.el.hasClass('active')){
32415                 r = p;
32416                 return false;
32417             }
32418             
32419             return;
32420         });
32421         
32422         return r;
32423     }
32424     
32425     
32426 });
32427
32428  
32429 /*
32430  * - LGPL
32431  *
32432  * Tab pane
32433  * 
32434  */
32435 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32436 /**
32437  * @class Roo.bootstrap.TabPane
32438  * @extends Roo.bootstrap.Component
32439  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32440  * Bootstrap TabPane class
32441  * @cfg {Boolean} active (false | true) Default false
32442  * @cfg {String} title title of panel
32443
32444  * 
32445  * @constructor
32446  * Create a new TabPane
32447  * @param {Object} config The config object
32448  */
32449
32450 Roo.bootstrap.dash.TabPane = function(config){
32451     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32452     
32453     this.addEvents({
32454         // raw events
32455         /**
32456          * @event activate
32457          * When a pane is activated
32458          * @param {Roo.bootstrap.dash.TabPane} pane
32459          */
32460         "activate" : true
32461          
32462     });
32463 };
32464
32465 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32466     
32467     active : false,
32468     title : '',
32469     
32470     // the tabBox that this is attached to.
32471     tab : false,
32472      
32473     getAutoCreate : function() 
32474     {
32475         var cfg = {
32476             tag: 'div',
32477             cls: 'tab-pane'
32478         };
32479         
32480         if(this.active){
32481             cfg.cls += ' active';
32482         }
32483         
32484         return cfg;
32485     },
32486     initEvents  : function()
32487     {
32488         //Roo.log('trigger add pane handler');
32489         this.parent().fireEvent('addpane', this)
32490     },
32491     
32492      /**
32493      * Updates the tab title 
32494      * @param {String} html to set the title to.
32495      */
32496     setTitle: function(str)
32497     {
32498         if (!this.tab) {
32499             return;
32500         }
32501         this.title = str;
32502         this.tab.select('a', true).first().dom.innerHTML = str;
32503         
32504     }
32505     
32506     
32507     
32508 });
32509
32510  
32511
32512
32513  /*
32514  * - LGPL
32515  *
32516  * Tooltip
32517  * 
32518  */
32519
32520 /**
32521  * @class Roo.bootstrap.Tooltip
32522  * Bootstrap Tooltip class
32523  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32524  * to determine which dom element triggers the tooltip.
32525  * 
32526  * It needs to add support for additional attributes like tooltip-position
32527  * 
32528  * @constructor
32529  * Create a new Toolti
32530  * @param {Object} config The config object
32531  */
32532
32533 Roo.bootstrap.Tooltip = function(config){
32534     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32535     
32536     this.alignment = Roo.bootstrap.Tooltip.alignment;
32537     
32538     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32539         this.alignment = config.alignment;
32540     }
32541     
32542 };
32543
32544 Roo.apply(Roo.bootstrap.Tooltip, {
32545     /**
32546      * @function init initialize tooltip monitoring.
32547      * @static
32548      */
32549     currentEl : false,
32550     currentTip : false,
32551     currentRegion : false,
32552     
32553     //  init : delay?
32554     
32555     init : function()
32556     {
32557         Roo.get(document).on('mouseover', this.enter ,this);
32558         Roo.get(document).on('mouseout', this.leave, this);
32559          
32560         
32561         this.currentTip = new Roo.bootstrap.Tooltip();
32562     },
32563     
32564     enter : function(ev)
32565     {
32566         var dom = ev.getTarget();
32567         
32568         //Roo.log(['enter',dom]);
32569         var el = Roo.fly(dom);
32570         if (this.currentEl) {
32571             //Roo.log(dom);
32572             //Roo.log(this.currentEl);
32573             //Roo.log(this.currentEl.contains(dom));
32574             if (this.currentEl == el) {
32575                 return;
32576             }
32577             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32578                 return;
32579             }
32580
32581         }
32582         
32583         if (this.currentTip.el) {
32584             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32585         }    
32586         //Roo.log(ev);
32587         
32588         if(!el || el.dom == document){
32589             return;
32590         }
32591         
32592         var bindEl = el; 
32593         var pel = false;
32594         if (!el.attr('tooltip')) {
32595             pel = el.findParent("[tooltip]");
32596             if (pel) {
32597                 bindEl = Roo.get(pel);
32598             }
32599         }
32600         
32601        
32602         
32603         // you can not look for children, as if el is the body.. then everythign is the child..
32604         if (!pel && !el.attr('tooltip')) { //
32605             if (!el.select("[tooltip]").elements.length) {
32606                 return;
32607             }
32608             // is the mouse over this child...?
32609             bindEl = el.select("[tooltip]").first();
32610             var xy = ev.getXY();
32611             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32612                 //Roo.log("not in region.");
32613                 return;
32614             }
32615             //Roo.log("child element over..");
32616             
32617         }
32618         this.currentEl = el;
32619         this.currentTip.bind(bindEl);
32620         this.currentRegion = Roo.lib.Region.getRegion(dom);
32621         this.currentTip.enter();
32622         
32623     },
32624     leave : function(ev)
32625     {
32626         var dom = ev.getTarget();
32627         //Roo.log(['leave',dom]);
32628         if (!this.currentEl) {
32629             return;
32630         }
32631         
32632         
32633         if (dom != this.currentEl.dom) {
32634             return;
32635         }
32636         var xy = ev.getXY();
32637         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32638             return;
32639         }
32640         // only activate leave if mouse cursor is outside... bounding box..
32641         
32642         
32643         
32644         
32645         if (this.currentTip) {
32646             this.currentTip.leave();
32647         }
32648         //Roo.log('clear currentEl');
32649         this.currentEl = false;
32650         
32651         
32652     },
32653     alignment : {
32654         'left' : ['r-l', [-2,0], 'right'],
32655         'right' : ['l-r', [2,0], 'left'],
32656         'bottom' : ['t-b', [0,2], 'top'],
32657         'top' : [ 'b-t', [0,-2], 'bottom']
32658     }
32659     
32660 });
32661
32662
32663 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32664     
32665     
32666     bindEl : false,
32667     
32668     delay : null, // can be { show : 300 , hide: 500}
32669     
32670     timeout : null,
32671     
32672     hoverState : null, //???
32673     
32674     placement : 'bottom', 
32675     
32676     alignment : false,
32677     
32678     getAutoCreate : function(){
32679     
32680         var cfg = {
32681            cls : 'tooltip',   
32682            role : 'tooltip',
32683            cn : [
32684                 {
32685                     cls : 'tooltip-arrow arrow'
32686                 },
32687                 {
32688                     cls : 'tooltip-inner'
32689                 }
32690            ]
32691         };
32692         
32693         return cfg;
32694     },
32695     bind : function(el)
32696     {
32697         this.bindEl = el;
32698     },
32699     
32700     initEvents : function()
32701     {
32702         this.arrowEl = this.el.select('.arrow', true).first();
32703         this.innerEl = this.el.select('.tooltip-inner', true).first();
32704     },
32705     
32706     enter : function () {
32707        
32708         if (this.timeout != null) {
32709             clearTimeout(this.timeout);
32710         }
32711         
32712         this.hoverState = 'in';
32713          //Roo.log("enter - show");
32714         if (!this.delay || !this.delay.show) {
32715             this.show();
32716             return;
32717         }
32718         var _t = this;
32719         this.timeout = setTimeout(function () {
32720             if (_t.hoverState == 'in') {
32721                 _t.show();
32722             }
32723         }, this.delay.show);
32724     },
32725     leave : function()
32726     {
32727         clearTimeout(this.timeout);
32728     
32729         this.hoverState = 'out';
32730          if (!this.delay || !this.delay.hide) {
32731             this.hide();
32732             return;
32733         }
32734        
32735         var _t = this;
32736         this.timeout = setTimeout(function () {
32737             //Roo.log("leave - timeout");
32738             
32739             if (_t.hoverState == 'out') {
32740                 _t.hide();
32741                 Roo.bootstrap.Tooltip.currentEl = false;
32742             }
32743         }, delay);
32744     },
32745     
32746     show : function (msg)
32747     {
32748         if (!this.el) {
32749             this.render(document.body);
32750         }
32751         // set content.
32752         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32753         
32754         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32755         
32756         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32757         
32758         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32759                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32760         
32761         var placement = typeof this.placement == 'function' ?
32762             this.placement.call(this, this.el, on_el) :
32763             this.placement;
32764             
32765         var autoToken = /\s?auto?\s?/i;
32766         var autoPlace = autoToken.test(placement);
32767         if (autoPlace) {
32768             placement = placement.replace(autoToken, '') || 'top';
32769         }
32770         
32771         //this.el.detach()
32772         //this.el.setXY([0,0]);
32773         this.el.show();
32774         //this.el.dom.style.display='block';
32775         
32776         //this.el.appendTo(on_el);
32777         
32778         var p = this.getPosition();
32779         var box = this.el.getBox();
32780         
32781         if (autoPlace) {
32782             // fixme..
32783         }
32784         
32785         var align = this.alignment[placement];
32786         
32787         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32788         
32789         if(placement == 'top' || placement == 'bottom'){
32790             if(xy[0] < 0){
32791                 placement = 'right';
32792             }
32793             
32794             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32795                 placement = 'left';
32796             }
32797             
32798             var scroll = Roo.select('body', true).first().getScroll();
32799             
32800             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32801                 placement = 'top';
32802             }
32803             
32804             align = this.alignment[placement];
32805             
32806             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32807             
32808         }
32809         
32810         var elems = document.getElementsByTagName('div');
32811         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32812         for (var i = 0; i < elems.length; i++) {
32813           var zindex = Number.parseInt(
32814                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32815                 10
32816           );
32817           if (zindex > highest) {
32818             highest = zindex;
32819           }
32820         }
32821         
32822         
32823         
32824         this.el.dom.style.zIndex = highest;
32825         
32826         this.el.alignTo(this.bindEl, align[0],align[1]);
32827         //var arrow = this.el.select('.arrow',true).first();
32828         //arrow.set(align[2], 
32829         
32830         this.el.addClass(placement);
32831         this.el.addClass("bs-tooltip-"+ placement);
32832         
32833         this.el.addClass('in fade show');
32834         
32835         this.hoverState = null;
32836         
32837         if (this.el.hasClass('fade')) {
32838             // fade it?
32839         }
32840         
32841         
32842         
32843         
32844         
32845     },
32846     hide : function()
32847     {
32848          
32849         if (!this.el) {
32850             return;
32851         }
32852         //this.el.setXY([0,0]);
32853         this.el.removeClass(['show', 'in']);
32854         //this.el.hide();
32855         
32856     }
32857     
32858 });
32859  
32860
32861  /*
32862  * - LGPL
32863  *
32864  * Location Picker
32865  * 
32866  */
32867
32868 /**
32869  * @class Roo.bootstrap.LocationPicker
32870  * @extends Roo.bootstrap.Component
32871  * Bootstrap LocationPicker class
32872  * @cfg {Number} latitude Position when init default 0
32873  * @cfg {Number} longitude Position when init default 0
32874  * @cfg {Number} zoom default 15
32875  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32876  * @cfg {Boolean} mapTypeControl default false
32877  * @cfg {Boolean} disableDoubleClickZoom default false
32878  * @cfg {Boolean} scrollwheel default true
32879  * @cfg {Boolean} streetViewControl default false
32880  * @cfg {Number} radius default 0
32881  * @cfg {String} locationName
32882  * @cfg {Boolean} draggable default true
32883  * @cfg {Boolean} enableAutocomplete default false
32884  * @cfg {Boolean} enableReverseGeocode default true
32885  * @cfg {String} markerTitle
32886  * 
32887  * @constructor
32888  * Create a new LocationPicker
32889  * @param {Object} config The config object
32890  */
32891
32892
32893 Roo.bootstrap.LocationPicker = function(config){
32894     
32895     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32896     
32897     this.addEvents({
32898         /**
32899          * @event initial
32900          * Fires when the picker initialized.
32901          * @param {Roo.bootstrap.LocationPicker} this
32902          * @param {Google Location} location
32903          */
32904         initial : true,
32905         /**
32906          * @event positionchanged
32907          * Fires when the picker position changed.
32908          * @param {Roo.bootstrap.LocationPicker} this
32909          * @param {Google Location} location
32910          */
32911         positionchanged : true,
32912         /**
32913          * @event resize
32914          * Fires when the map resize.
32915          * @param {Roo.bootstrap.LocationPicker} this
32916          */
32917         resize : true,
32918         /**
32919          * @event show
32920          * Fires when the map show.
32921          * @param {Roo.bootstrap.LocationPicker} this
32922          */
32923         show : true,
32924         /**
32925          * @event hide
32926          * Fires when the map hide.
32927          * @param {Roo.bootstrap.LocationPicker} this
32928          */
32929         hide : true,
32930         /**
32931          * @event mapClick
32932          * Fires when click the map.
32933          * @param {Roo.bootstrap.LocationPicker} this
32934          * @param {Map event} e
32935          */
32936         mapClick : true,
32937         /**
32938          * @event mapRightClick
32939          * Fires when right click the map.
32940          * @param {Roo.bootstrap.LocationPicker} this
32941          * @param {Map event} e
32942          */
32943         mapRightClick : true,
32944         /**
32945          * @event markerClick
32946          * Fires when click the marker.
32947          * @param {Roo.bootstrap.LocationPicker} this
32948          * @param {Map event} e
32949          */
32950         markerClick : true,
32951         /**
32952          * @event markerRightClick
32953          * Fires when right click the marker.
32954          * @param {Roo.bootstrap.LocationPicker} this
32955          * @param {Map event} e
32956          */
32957         markerRightClick : true,
32958         /**
32959          * @event OverlayViewDraw
32960          * Fires when OverlayView Draw
32961          * @param {Roo.bootstrap.LocationPicker} this
32962          */
32963         OverlayViewDraw : true,
32964         /**
32965          * @event OverlayViewOnAdd
32966          * Fires when OverlayView Draw
32967          * @param {Roo.bootstrap.LocationPicker} this
32968          */
32969         OverlayViewOnAdd : true,
32970         /**
32971          * @event OverlayViewOnRemove
32972          * Fires when OverlayView Draw
32973          * @param {Roo.bootstrap.LocationPicker} this
32974          */
32975         OverlayViewOnRemove : true,
32976         /**
32977          * @event OverlayViewShow
32978          * Fires when OverlayView Draw
32979          * @param {Roo.bootstrap.LocationPicker} this
32980          * @param {Pixel} cpx
32981          */
32982         OverlayViewShow : true,
32983         /**
32984          * @event OverlayViewHide
32985          * Fires when OverlayView Draw
32986          * @param {Roo.bootstrap.LocationPicker} this
32987          */
32988         OverlayViewHide : true,
32989         /**
32990          * @event loadexception
32991          * Fires when load google lib failed.
32992          * @param {Roo.bootstrap.LocationPicker} this
32993          */
32994         loadexception : true
32995     });
32996         
32997 };
32998
32999 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33000     
33001     gMapContext: false,
33002     
33003     latitude: 0,
33004     longitude: 0,
33005     zoom: 15,
33006     mapTypeId: false,
33007     mapTypeControl: false,
33008     disableDoubleClickZoom: false,
33009     scrollwheel: true,
33010     streetViewControl: false,
33011     radius: 0,
33012     locationName: '',
33013     draggable: true,
33014     enableAutocomplete: false,
33015     enableReverseGeocode: true,
33016     markerTitle: '',
33017     
33018     getAutoCreate: function()
33019     {
33020
33021         var cfg = {
33022             tag: 'div',
33023             cls: 'roo-location-picker'
33024         };
33025         
33026         return cfg
33027     },
33028     
33029     initEvents: function(ct, position)
33030     {       
33031         if(!this.el.getWidth() || this.isApplied()){
33032             return;
33033         }
33034         
33035         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33036         
33037         this.initial();
33038     },
33039     
33040     initial: function()
33041     {
33042         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33043             this.fireEvent('loadexception', this);
33044             return;
33045         }
33046         
33047         if(!this.mapTypeId){
33048             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33049         }
33050         
33051         this.gMapContext = this.GMapContext();
33052         
33053         this.initOverlayView();
33054         
33055         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33056         
33057         var _this = this;
33058                 
33059         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33060             _this.setPosition(_this.gMapContext.marker.position);
33061         });
33062         
33063         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33064             _this.fireEvent('mapClick', this, event);
33065             
33066         });
33067
33068         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33069             _this.fireEvent('mapRightClick', this, event);
33070             
33071         });
33072         
33073         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33074             _this.fireEvent('markerClick', this, event);
33075             
33076         });
33077
33078         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33079             _this.fireEvent('markerRightClick', this, event);
33080             
33081         });
33082         
33083         this.setPosition(this.gMapContext.location);
33084         
33085         this.fireEvent('initial', this, this.gMapContext.location);
33086     },
33087     
33088     initOverlayView: function()
33089     {
33090         var _this = this;
33091         
33092         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33093             
33094             draw: function()
33095             {
33096                 _this.fireEvent('OverlayViewDraw', _this);
33097             },
33098             
33099             onAdd: function()
33100             {
33101                 _this.fireEvent('OverlayViewOnAdd', _this);
33102             },
33103             
33104             onRemove: function()
33105             {
33106                 _this.fireEvent('OverlayViewOnRemove', _this);
33107             },
33108             
33109             show: function(cpx)
33110             {
33111                 _this.fireEvent('OverlayViewShow', _this, cpx);
33112             },
33113             
33114             hide: function()
33115             {
33116                 _this.fireEvent('OverlayViewHide', _this);
33117             }
33118             
33119         });
33120     },
33121     
33122     fromLatLngToContainerPixel: function(event)
33123     {
33124         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33125     },
33126     
33127     isApplied: function() 
33128     {
33129         return this.getGmapContext() == false ? false : true;
33130     },
33131     
33132     getGmapContext: function() 
33133     {
33134         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33135     },
33136     
33137     GMapContext: function() 
33138     {
33139         var position = new google.maps.LatLng(this.latitude, this.longitude);
33140         
33141         var _map = new google.maps.Map(this.el.dom, {
33142             center: position,
33143             zoom: this.zoom,
33144             mapTypeId: this.mapTypeId,
33145             mapTypeControl: this.mapTypeControl,
33146             disableDoubleClickZoom: this.disableDoubleClickZoom,
33147             scrollwheel: this.scrollwheel,
33148             streetViewControl: this.streetViewControl,
33149             locationName: this.locationName,
33150             draggable: this.draggable,
33151             enableAutocomplete: this.enableAutocomplete,
33152             enableReverseGeocode: this.enableReverseGeocode
33153         });
33154         
33155         var _marker = new google.maps.Marker({
33156             position: position,
33157             map: _map,
33158             title: this.markerTitle,
33159             draggable: this.draggable
33160         });
33161         
33162         return {
33163             map: _map,
33164             marker: _marker,
33165             circle: null,
33166             location: position,
33167             radius: this.radius,
33168             locationName: this.locationName,
33169             addressComponents: {
33170                 formatted_address: null,
33171                 addressLine1: null,
33172                 addressLine2: null,
33173                 streetName: null,
33174                 streetNumber: null,
33175                 city: null,
33176                 district: null,
33177                 state: null,
33178                 stateOrProvince: null
33179             },
33180             settings: this,
33181             domContainer: this.el.dom,
33182             geodecoder: new google.maps.Geocoder()
33183         };
33184     },
33185     
33186     drawCircle: function(center, radius, options) 
33187     {
33188         if (this.gMapContext.circle != null) {
33189             this.gMapContext.circle.setMap(null);
33190         }
33191         if (radius > 0) {
33192             radius *= 1;
33193             options = Roo.apply({}, options, {
33194                 strokeColor: "#0000FF",
33195                 strokeOpacity: .35,
33196                 strokeWeight: 2,
33197                 fillColor: "#0000FF",
33198                 fillOpacity: .2
33199             });
33200             
33201             options.map = this.gMapContext.map;
33202             options.radius = radius;
33203             options.center = center;
33204             this.gMapContext.circle = new google.maps.Circle(options);
33205             return this.gMapContext.circle;
33206         }
33207         
33208         return null;
33209     },
33210     
33211     setPosition: function(location) 
33212     {
33213         this.gMapContext.location = location;
33214         this.gMapContext.marker.setPosition(location);
33215         this.gMapContext.map.panTo(location);
33216         this.drawCircle(location, this.gMapContext.radius, {});
33217         
33218         var _this = this;
33219         
33220         if (this.gMapContext.settings.enableReverseGeocode) {
33221             this.gMapContext.geodecoder.geocode({
33222                 latLng: this.gMapContext.location
33223             }, function(results, status) {
33224                 
33225                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33226                     _this.gMapContext.locationName = results[0].formatted_address;
33227                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33228                     
33229                     _this.fireEvent('positionchanged', this, location);
33230                 }
33231             });
33232             
33233             return;
33234         }
33235         
33236         this.fireEvent('positionchanged', this, location);
33237     },
33238     
33239     resize: function()
33240     {
33241         google.maps.event.trigger(this.gMapContext.map, "resize");
33242         
33243         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33244         
33245         this.fireEvent('resize', this);
33246     },
33247     
33248     setPositionByLatLng: function(latitude, longitude)
33249     {
33250         this.setPosition(new google.maps.LatLng(latitude, longitude));
33251     },
33252     
33253     getCurrentPosition: function() 
33254     {
33255         return {
33256             latitude: this.gMapContext.location.lat(),
33257             longitude: this.gMapContext.location.lng()
33258         };
33259     },
33260     
33261     getAddressName: function() 
33262     {
33263         return this.gMapContext.locationName;
33264     },
33265     
33266     getAddressComponents: function() 
33267     {
33268         return this.gMapContext.addressComponents;
33269     },
33270     
33271     address_component_from_google_geocode: function(address_components) 
33272     {
33273         var result = {};
33274         
33275         for (var i = 0; i < address_components.length; i++) {
33276             var component = address_components[i];
33277             if (component.types.indexOf("postal_code") >= 0) {
33278                 result.postalCode = component.short_name;
33279             } else if (component.types.indexOf("street_number") >= 0) {
33280                 result.streetNumber = component.short_name;
33281             } else if (component.types.indexOf("route") >= 0) {
33282                 result.streetName = component.short_name;
33283             } else if (component.types.indexOf("neighborhood") >= 0) {
33284                 result.city = component.short_name;
33285             } else if (component.types.indexOf("locality") >= 0) {
33286                 result.city = component.short_name;
33287             } else if (component.types.indexOf("sublocality") >= 0) {
33288                 result.district = component.short_name;
33289             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33290                 result.stateOrProvince = component.short_name;
33291             } else if (component.types.indexOf("country") >= 0) {
33292                 result.country = component.short_name;
33293             }
33294         }
33295         
33296         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33297         result.addressLine2 = "";
33298         return result;
33299     },
33300     
33301     setZoomLevel: function(zoom)
33302     {
33303         this.gMapContext.map.setZoom(zoom);
33304     },
33305     
33306     show: function()
33307     {
33308         if(!this.el){
33309             return;
33310         }
33311         
33312         this.el.show();
33313         
33314         this.resize();
33315         
33316         this.fireEvent('show', this);
33317     },
33318     
33319     hide: function()
33320     {
33321         if(!this.el){
33322             return;
33323         }
33324         
33325         this.el.hide();
33326         
33327         this.fireEvent('hide', this);
33328     }
33329     
33330 });
33331
33332 Roo.apply(Roo.bootstrap.LocationPicker, {
33333     
33334     OverlayView : function(map, options)
33335     {
33336         options = options || {};
33337         
33338         this.setMap(map);
33339     }
33340     
33341     
33342 });/**
33343  * @class Roo.bootstrap.Alert
33344  * @extends Roo.bootstrap.Component
33345  * Bootstrap Alert class - shows an alert area box
33346  * eg
33347  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33348   Enter a valid email address
33349 </div>
33350  * @licence LGPL
33351  * @cfg {String} title The title of alert
33352  * @cfg {String} html The content of alert
33353  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33354  * @cfg {String} fa font-awesomeicon
33355  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33356  * @cfg {Boolean} close true to show a x closer
33357  * 
33358  * 
33359  * @constructor
33360  * Create a new alert
33361  * @param {Object} config The config object
33362  */
33363
33364
33365 Roo.bootstrap.Alert = function(config){
33366     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33367     
33368 };
33369
33370 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33371     
33372     title: '',
33373     html: '',
33374     weight: false,
33375     fa: false,
33376     faicon: false, // BC
33377     close : false,
33378     
33379     
33380     getAutoCreate : function()
33381     {
33382         
33383         var cfg = {
33384             tag : 'div',
33385             cls : 'alert',
33386             cn : [
33387                 {
33388                     tag: 'button',
33389                     type :  "button",
33390                     cls: "close",
33391                     html : '×',
33392                     style : this.close ? '' : 'display:none'
33393                 },
33394                 {
33395                     tag : 'i',
33396                     cls : 'roo-alert-icon'
33397                     
33398                 },
33399                 {
33400                     tag : 'b',
33401                     cls : 'roo-alert-title',
33402                     html : this.title
33403                 },
33404                 {
33405                     tag : 'span',
33406                     cls : 'roo-alert-text',
33407                     html : this.html
33408                 }
33409             ]
33410         };
33411         
33412         if(this.faicon){
33413             cfg.cn[0].cls += ' fa ' + this.faicon;
33414         }
33415         if(this.fa){
33416             cfg.cn[0].cls += ' fa ' + this.fa;
33417         }
33418         
33419         if(this.weight){
33420             cfg.cls += ' alert-' + this.weight;
33421         }
33422         
33423         return cfg;
33424     },
33425     
33426     initEvents: function() 
33427     {
33428         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33429         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33430         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33431         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33432         if (this.seconds > 0) {
33433             this.hide.defer(this.seconds, this);
33434         }
33435     },
33436     /**
33437      * Set the Title Message HTML
33438      * @param {String} html
33439      */
33440     setTitle : function(str)
33441     {
33442         this.titleEl.dom.innerHTML = str;
33443     },
33444      
33445      /**
33446      * Set the Body Message HTML
33447      * @param {String} html
33448      */
33449     setHtml : function(str)
33450     {
33451         this.htmlEl.dom.innerHTML = str;
33452     },
33453     /**
33454      * Set the Weight of the alert
33455      * @param {String} (success|info|warning|danger) weight
33456      */
33457     
33458     setWeight : function(weight)
33459     {
33460         if(this.weight){
33461             this.el.removeClass('alert-' + this.weight);
33462         }
33463         
33464         this.weight = weight;
33465         
33466         this.el.addClass('alert-' + this.weight);
33467     },
33468       /**
33469      * Set the Icon of the alert
33470      * @param {String} see fontawsome names (name without the 'fa-' bit)
33471      */
33472     setIcon : function(icon)
33473     {
33474         if(this.faicon){
33475             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33476         }
33477         
33478         this.faicon = icon;
33479         
33480         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33481     },
33482     /**
33483      * Hide the Alert
33484      */
33485     hide: function() 
33486     {
33487         this.el.hide();   
33488     },
33489     /**
33490      * Show the Alert
33491      */
33492     show: function() 
33493     {  
33494         this.el.show();   
33495     }
33496     
33497 });
33498
33499  
33500 /*
33501 * Licence: LGPL
33502 */
33503
33504 /**
33505  * @class Roo.bootstrap.UploadCropbox
33506  * @extends Roo.bootstrap.Component
33507  * Bootstrap UploadCropbox class
33508  * @cfg {String} emptyText show when image has been loaded
33509  * @cfg {String} rotateNotify show when image too small to rotate
33510  * @cfg {Number} errorTimeout default 3000
33511  * @cfg {Number} minWidth default 300
33512  * @cfg {Number} minHeight default 300
33513  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33514  * @cfg {Boolean} isDocument (true|false) default false
33515  * @cfg {String} url action url
33516  * @cfg {String} paramName default 'imageUpload'
33517  * @cfg {String} method default POST
33518  * @cfg {Boolean} loadMask (true|false) default true
33519  * @cfg {Boolean} loadingText default 'Loading...'
33520  * 
33521  * @constructor
33522  * Create a new UploadCropbox
33523  * @param {Object} config The config object
33524  */
33525
33526 Roo.bootstrap.UploadCropbox = function(config){
33527     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33528     
33529     this.addEvents({
33530         /**
33531          * @event beforeselectfile
33532          * Fire before select file
33533          * @param {Roo.bootstrap.UploadCropbox} this
33534          */
33535         "beforeselectfile" : true,
33536         /**
33537          * @event initial
33538          * Fire after initEvent
33539          * @param {Roo.bootstrap.UploadCropbox} this
33540          */
33541         "initial" : true,
33542         /**
33543          * @event crop
33544          * Fire after initEvent
33545          * @param {Roo.bootstrap.UploadCropbox} this
33546          * @param {String} data
33547          */
33548         "crop" : true,
33549         /**
33550          * @event prepare
33551          * Fire when preparing the file data
33552          * @param {Roo.bootstrap.UploadCropbox} this
33553          * @param {Object} file
33554          */
33555         "prepare" : true,
33556         /**
33557          * @event exception
33558          * Fire when get exception
33559          * @param {Roo.bootstrap.UploadCropbox} this
33560          * @param {XMLHttpRequest} xhr
33561          */
33562         "exception" : true,
33563         /**
33564          * @event beforeloadcanvas
33565          * Fire before load the canvas
33566          * @param {Roo.bootstrap.UploadCropbox} this
33567          * @param {String} src
33568          */
33569         "beforeloadcanvas" : true,
33570         /**
33571          * @event trash
33572          * Fire when trash image
33573          * @param {Roo.bootstrap.UploadCropbox} this
33574          */
33575         "trash" : true,
33576         /**
33577          * @event download
33578          * Fire when download the image
33579          * @param {Roo.bootstrap.UploadCropbox} this
33580          */
33581         "download" : true,
33582         /**
33583          * @event footerbuttonclick
33584          * Fire when footerbuttonclick
33585          * @param {Roo.bootstrap.UploadCropbox} this
33586          * @param {String} type
33587          */
33588         "footerbuttonclick" : true,
33589         /**
33590          * @event resize
33591          * Fire when resize
33592          * @param {Roo.bootstrap.UploadCropbox} this
33593          */
33594         "resize" : true,
33595         /**
33596          * @event rotate
33597          * Fire when rotate the image
33598          * @param {Roo.bootstrap.UploadCropbox} this
33599          * @param {String} pos
33600          */
33601         "rotate" : true,
33602         /**
33603          * @event inspect
33604          * Fire when inspect the file
33605          * @param {Roo.bootstrap.UploadCropbox} this
33606          * @param {Object} file
33607          */
33608         "inspect" : true,
33609         /**
33610          * @event upload
33611          * Fire when xhr upload the file
33612          * @param {Roo.bootstrap.UploadCropbox} this
33613          * @param {Object} data
33614          */
33615         "upload" : true,
33616         /**
33617          * @event arrange
33618          * Fire when arrange the file data
33619          * @param {Roo.bootstrap.UploadCropbox} this
33620          * @param {Object} formData
33621          */
33622         "arrange" : true
33623     });
33624     
33625     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33626 };
33627
33628 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33629     
33630     emptyText : 'Click to upload image',
33631     rotateNotify : 'Image is too small to rotate',
33632     errorTimeout : 3000,
33633     scale : 0,
33634     baseScale : 1,
33635     rotate : 0,
33636     dragable : false,
33637     pinching : false,
33638     mouseX : 0,
33639     mouseY : 0,
33640     cropData : false,
33641     minWidth : 300,
33642     minHeight : 300,
33643     file : false,
33644     exif : {},
33645     baseRotate : 1,
33646     cropType : 'image/jpeg',
33647     buttons : false,
33648     canvasLoaded : false,
33649     isDocument : false,
33650     method : 'POST',
33651     paramName : 'imageUpload',
33652     loadMask : true,
33653     loadingText : 'Loading...',
33654     maskEl : false,
33655     
33656     getAutoCreate : function()
33657     {
33658         var cfg = {
33659             tag : 'div',
33660             cls : 'roo-upload-cropbox',
33661             cn : [
33662                 {
33663                     tag : 'input',
33664                     cls : 'roo-upload-cropbox-selector',
33665                     type : 'file'
33666                 },
33667                 {
33668                     tag : 'div',
33669                     cls : 'roo-upload-cropbox-body',
33670                     style : 'cursor:pointer',
33671                     cn : [
33672                         {
33673                             tag : 'div',
33674                             cls : 'roo-upload-cropbox-preview'
33675                         },
33676                         {
33677                             tag : 'div',
33678                             cls : 'roo-upload-cropbox-thumb'
33679                         },
33680                         {
33681                             tag : 'div',
33682                             cls : 'roo-upload-cropbox-empty-notify',
33683                             html : this.emptyText
33684                         },
33685                         {
33686                             tag : 'div',
33687                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33688                             html : this.rotateNotify
33689                         }
33690                     ]
33691                 },
33692                 {
33693                     tag : 'div',
33694                     cls : 'roo-upload-cropbox-footer',
33695                     cn : {
33696                         tag : 'div',
33697                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33698                         cn : []
33699                     }
33700                 }
33701             ]
33702         };
33703         
33704         return cfg;
33705     },
33706     
33707     onRender : function(ct, position)
33708     {
33709         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33710         
33711         if (this.buttons.length) {
33712             
33713             Roo.each(this.buttons, function(bb) {
33714                 
33715                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33716                 
33717                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33718                 
33719             }, this);
33720         }
33721         
33722         if(this.loadMask){
33723             this.maskEl = this.el;
33724         }
33725     },
33726     
33727     initEvents : function()
33728     {
33729         this.urlAPI = (window.createObjectURL && window) || 
33730                                 (window.URL && URL.revokeObjectURL && URL) || 
33731                                 (window.webkitURL && webkitURL);
33732                         
33733         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33734         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33735         
33736         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33737         this.selectorEl.hide();
33738         
33739         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33740         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33741         
33742         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33743         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33744         this.thumbEl.hide();
33745         
33746         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33747         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33748         
33749         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33750         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33751         this.errorEl.hide();
33752         
33753         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33754         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33755         this.footerEl.hide();
33756         
33757         this.setThumbBoxSize();
33758         
33759         this.bind();
33760         
33761         this.resize();
33762         
33763         this.fireEvent('initial', this);
33764     },
33765
33766     bind : function()
33767     {
33768         var _this = this;
33769         
33770         window.addEventListener("resize", function() { _this.resize(); } );
33771         
33772         this.bodyEl.on('click', this.beforeSelectFile, this);
33773         
33774         if(Roo.isTouch){
33775             this.bodyEl.on('touchstart', this.onTouchStart, this);
33776             this.bodyEl.on('touchmove', this.onTouchMove, this);
33777             this.bodyEl.on('touchend', this.onTouchEnd, this);
33778         }
33779         
33780         if(!Roo.isTouch){
33781             this.bodyEl.on('mousedown', this.onMouseDown, this);
33782             this.bodyEl.on('mousemove', this.onMouseMove, this);
33783             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33784             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33785             Roo.get(document).on('mouseup', this.onMouseUp, this);
33786         }
33787         
33788         this.selectorEl.on('change', this.onFileSelected, this);
33789     },
33790     
33791     reset : function()
33792     {    
33793         this.scale = 0;
33794         this.baseScale = 1;
33795         this.rotate = 0;
33796         this.baseRotate = 1;
33797         this.dragable = false;
33798         this.pinching = false;
33799         this.mouseX = 0;
33800         this.mouseY = 0;
33801         this.cropData = false;
33802         this.notifyEl.dom.innerHTML = this.emptyText;
33803         
33804         this.selectorEl.dom.value = '';
33805         
33806     },
33807     
33808     resize : function()
33809     {
33810         if(this.fireEvent('resize', this) != false){
33811             this.setThumbBoxPosition();
33812             this.setCanvasPosition();
33813         }
33814     },
33815     
33816     onFooterButtonClick : function(e, el, o, type)
33817     {
33818         switch (type) {
33819             case 'rotate-left' :
33820                 this.onRotateLeft(e);
33821                 break;
33822             case 'rotate-right' :
33823                 this.onRotateRight(e);
33824                 break;
33825             case 'picture' :
33826                 this.beforeSelectFile(e);
33827                 break;
33828             case 'trash' :
33829                 this.trash(e);
33830                 break;
33831             case 'crop' :
33832                 this.crop(e);
33833                 break;
33834             case 'download' :
33835                 this.download(e);
33836                 break;
33837             default :
33838                 break;
33839         }
33840         
33841         this.fireEvent('footerbuttonclick', this, type);
33842     },
33843     
33844     beforeSelectFile : function(e)
33845     {
33846         e.preventDefault();
33847         
33848         if(this.fireEvent('beforeselectfile', this) != false){
33849             this.selectorEl.dom.click();
33850         }
33851     },
33852     
33853     onFileSelected : function(e)
33854     {
33855         e.preventDefault();
33856         
33857         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33858             return;
33859         }
33860         
33861         var file = this.selectorEl.dom.files[0];
33862         
33863         if(this.fireEvent('inspect', this, file) != false){
33864             this.prepare(file);
33865         }
33866         
33867     },
33868     
33869     trash : function(e)
33870     {
33871         this.fireEvent('trash', this);
33872     },
33873     
33874     download : function(e)
33875     {
33876         this.fireEvent('download', this);
33877     },
33878     
33879     loadCanvas : function(src)
33880     {   
33881         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33882             
33883             this.reset();
33884             
33885             this.imageEl = document.createElement('img');
33886             
33887             var _this = this;
33888             
33889             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33890             
33891             this.imageEl.src = src;
33892         }
33893     },
33894     
33895     onLoadCanvas : function()
33896     {   
33897         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33898         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33899         
33900         this.bodyEl.un('click', this.beforeSelectFile, this);
33901         
33902         this.notifyEl.hide();
33903         this.thumbEl.show();
33904         this.footerEl.show();
33905         
33906         this.baseRotateLevel();
33907         
33908         if(this.isDocument){
33909             this.setThumbBoxSize();
33910         }
33911         
33912         this.setThumbBoxPosition();
33913         
33914         this.baseScaleLevel();
33915         
33916         this.draw();
33917         
33918         this.resize();
33919         
33920         this.canvasLoaded = true;
33921         
33922         if(this.loadMask){
33923             this.maskEl.unmask();
33924         }
33925         
33926     },
33927     
33928     setCanvasPosition : function()
33929     {   
33930         if(!this.canvasEl){
33931             return;
33932         }
33933         
33934         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33935         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33936         
33937         this.previewEl.setLeft(pw);
33938         this.previewEl.setTop(ph);
33939         
33940     },
33941     
33942     onMouseDown : function(e)
33943     {   
33944         e.stopEvent();
33945         
33946         this.dragable = true;
33947         this.pinching = false;
33948         
33949         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33950             this.dragable = false;
33951             return;
33952         }
33953         
33954         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33955         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33956         
33957     },
33958     
33959     onMouseMove : function(e)
33960     {   
33961         e.stopEvent();
33962         
33963         if(!this.canvasLoaded){
33964             return;
33965         }
33966         
33967         if (!this.dragable){
33968             return;
33969         }
33970         
33971         var minX = Math.ceil(this.thumbEl.getLeft(true));
33972         var minY = Math.ceil(this.thumbEl.getTop(true));
33973         
33974         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33975         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33976         
33977         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33978         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33979         
33980         x = x - this.mouseX;
33981         y = y - this.mouseY;
33982         
33983         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33984         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33985         
33986         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33987         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33988         
33989         this.previewEl.setLeft(bgX);
33990         this.previewEl.setTop(bgY);
33991         
33992         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33993         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33994     },
33995     
33996     onMouseUp : function(e)
33997     {   
33998         e.stopEvent();
33999         
34000         this.dragable = false;
34001     },
34002     
34003     onMouseWheel : function(e)
34004     {   
34005         e.stopEvent();
34006         
34007         this.startScale = this.scale;
34008         
34009         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34010         
34011         if(!this.zoomable()){
34012             this.scale = this.startScale;
34013             return;
34014         }
34015         
34016         this.draw();
34017         
34018         return;
34019     },
34020     
34021     zoomable : function()
34022     {
34023         var minScale = this.thumbEl.getWidth() / this.minWidth;
34024         
34025         if(this.minWidth < this.minHeight){
34026             minScale = this.thumbEl.getHeight() / this.minHeight;
34027         }
34028         
34029         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34030         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34031         
34032         if(
34033                 this.isDocument &&
34034                 (this.rotate == 0 || this.rotate == 180) && 
34035                 (
34036                     width > this.imageEl.OriginWidth || 
34037                     height > this.imageEl.OriginHeight ||
34038                     (width < this.minWidth && height < this.minHeight)
34039                 )
34040         ){
34041             return false;
34042         }
34043         
34044         if(
34045                 this.isDocument &&
34046                 (this.rotate == 90 || this.rotate == 270) && 
34047                 (
34048                     width > this.imageEl.OriginWidth || 
34049                     height > this.imageEl.OriginHeight ||
34050                     (width < this.minHeight && height < this.minWidth)
34051                 )
34052         ){
34053             return false;
34054         }
34055         
34056         if(
34057                 !this.isDocument &&
34058                 (this.rotate == 0 || this.rotate == 180) && 
34059                 (
34060                     width < this.minWidth || 
34061                     width > this.imageEl.OriginWidth || 
34062                     height < this.minHeight || 
34063                     height > this.imageEl.OriginHeight
34064                 )
34065         ){
34066             return false;
34067         }
34068         
34069         if(
34070                 !this.isDocument &&
34071                 (this.rotate == 90 || this.rotate == 270) && 
34072                 (
34073                     width < this.minHeight || 
34074                     width > this.imageEl.OriginWidth || 
34075                     height < this.minWidth || 
34076                     height > this.imageEl.OriginHeight
34077                 )
34078         ){
34079             return false;
34080         }
34081         
34082         return true;
34083         
34084     },
34085     
34086     onRotateLeft : function(e)
34087     {   
34088         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34089             
34090             var minScale = this.thumbEl.getWidth() / this.minWidth;
34091             
34092             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34093             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34094             
34095             this.startScale = this.scale;
34096             
34097             while (this.getScaleLevel() < minScale){
34098             
34099                 this.scale = this.scale + 1;
34100                 
34101                 if(!this.zoomable()){
34102                     break;
34103                 }
34104                 
34105                 if(
34106                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34107                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34108                 ){
34109                     continue;
34110                 }
34111                 
34112                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34113
34114                 this.draw();
34115                 
34116                 return;
34117             }
34118             
34119             this.scale = this.startScale;
34120             
34121             this.onRotateFail();
34122             
34123             return false;
34124         }
34125         
34126         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34127
34128         if(this.isDocument){
34129             this.setThumbBoxSize();
34130             this.setThumbBoxPosition();
34131             this.setCanvasPosition();
34132         }
34133         
34134         this.draw();
34135         
34136         this.fireEvent('rotate', this, 'left');
34137         
34138     },
34139     
34140     onRotateRight : function(e)
34141     {
34142         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34143             
34144             var minScale = this.thumbEl.getWidth() / this.minWidth;
34145         
34146             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34147             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34148             
34149             this.startScale = this.scale;
34150             
34151             while (this.getScaleLevel() < minScale){
34152             
34153                 this.scale = this.scale + 1;
34154                 
34155                 if(!this.zoomable()){
34156                     break;
34157                 }
34158                 
34159                 if(
34160                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34161                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34162                 ){
34163                     continue;
34164                 }
34165                 
34166                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34167
34168                 this.draw();
34169                 
34170                 return;
34171             }
34172             
34173             this.scale = this.startScale;
34174             
34175             this.onRotateFail();
34176             
34177             return false;
34178         }
34179         
34180         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34181
34182         if(this.isDocument){
34183             this.setThumbBoxSize();
34184             this.setThumbBoxPosition();
34185             this.setCanvasPosition();
34186         }
34187         
34188         this.draw();
34189         
34190         this.fireEvent('rotate', this, 'right');
34191     },
34192     
34193     onRotateFail : function()
34194     {
34195         this.errorEl.show(true);
34196         
34197         var _this = this;
34198         
34199         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34200     },
34201     
34202     draw : function()
34203     {
34204         this.previewEl.dom.innerHTML = '';
34205         
34206         var canvasEl = document.createElement("canvas");
34207         
34208         var contextEl = canvasEl.getContext("2d");
34209         
34210         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34211         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34212         var center = this.imageEl.OriginWidth / 2;
34213         
34214         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34215             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34216             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34217             center = this.imageEl.OriginHeight / 2;
34218         }
34219         
34220         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34221         
34222         contextEl.translate(center, center);
34223         contextEl.rotate(this.rotate * Math.PI / 180);
34224
34225         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34226         
34227         this.canvasEl = document.createElement("canvas");
34228         
34229         this.contextEl = this.canvasEl.getContext("2d");
34230         
34231         switch (this.rotate) {
34232             case 0 :
34233                 
34234                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34235                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34236                 
34237                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34238                 
34239                 break;
34240             case 90 : 
34241                 
34242                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34243                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34244                 
34245                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34246                     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);
34247                     break;
34248                 }
34249                 
34250                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34251                 
34252                 break;
34253             case 180 :
34254                 
34255                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34256                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34257                 
34258                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34259                     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);
34260                     break;
34261                 }
34262                 
34263                 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);
34264                 
34265                 break;
34266             case 270 :
34267                 
34268                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34269                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34270         
34271                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34272                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34273                     break;
34274                 }
34275                 
34276                 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);
34277                 
34278                 break;
34279             default : 
34280                 break;
34281         }
34282         
34283         this.previewEl.appendChild(this.canvasEl);
34284         
34285         this.setCanvasPosition();
34286     },
34287     
34288     crop : function()
34289     {
34290         if(!this.canvasLoaded){
34291             return;
34292         }
34293         
34294         var imageCanvas = document.createElement("canvas");
34295         
34296         var imageContext = imageCanvas.getContext("2d");
34297         
34298         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34299         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34300         
34301         var center = imageCanvas.width / 2;
34302         
34303         imageContext.translate(center, center);
34304         
34305         imageContext.rotate(this.rotate * Math.PI / 180);
34306         
34307         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34308         
34309         var canvas = document.createElement("canvas");
34310         
34311         var context = canvas.getContext("2d");
34312                 
34313         canvas.width = this.minWidth;
34314         canvas.height = this.minHeight;
34315
34316         switch (this.rotate) {
34317             case 0 :
34318                 
34319                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34320                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34321                 
34322                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34323                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34324                 
34325                 var targetWidth = this.minWidth - 2 * x;
34326                 var targetHeight = this.minHeight - 2 * y;
34327                 
34328                 var scale = 1;
34329                 
34330                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34331                     scale = targetWidth / width;
34332                 }
34333                 
34334                 if(x > 0 && y == 0){
34335                     scale = targetHeight / height;
34336                 }
34337                 
34338                 if(x > 0 && y > 0){
34339                     scale = targetWidth / width;
34340                     
34341                     if(width < height){
34342                         scale = targetHeight / height;
34343                     }
34344                 }
34345                 
34346                 context.scale(scale, scale);
34347                 
34348                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34349                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34350
34351                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34352                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34353
34354                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34355                 
34356                 break;
34357             case 90 : 
34358                 
34359                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34360                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34361                 
34362                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34363                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34364                 
34365                 var targetWidth = this.minWidth - 2 * x;
34366                 var targetHeight = this.minHeight - 2 * y;
34367                 
34368                 var scale = 1;
34369                 
34370                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34371                     scale = targetWidth / width;
34372                 }
34373                 
34374                 if(x > 0 && y == 0){
34375                     scale = targetHeight / height;
34376                 }
34377                 
34378                 if(x > 0 && y > 0){
34379                     scale = targetWidth / width;
34380                     
34381                     if(width < height){
34382                         scale = targetHeight / height;
34383                     }
34384                 }
34385                 
34386                 context.scale(scale, scale);
34387                 
34388                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34389                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34390
34391                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34392                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34393                 
34394                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34395                 
34396                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34397                 
34398                 break;
34399             case 180 :
34400                 
34401                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34402                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34403                 
34404                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34405                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34406                 
34407                 var targetWidth = this.minWidth - 2 * x;
34408                 var targetHeight = this.minHeight - 2 * y;
34409                 
34410                 var scale = 1;
34411                 
34412                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34413                     scale = targetWidth / width;
34414                 }
34415                 
34416                 if(x > 0 && y == 0){
34417                     scale = targetHeight / height;
34418                 }
34419                 
34420                 if(x > 0 && y > 0){
34421                     scale = targetWidth / width;
34422                     
34423                     if(width < height){
34424                         scale = targetHeight / height;
34425                     }
34426                 }
34427                 
34428                 context.scale(scale, scale);
34429                 
34430                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34431                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34432
34433                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34434                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34435
34436                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34437                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34438                 
34439                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34440                 
34441                 break;
34442             case 270 :
34443                 
34444                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34445                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34446                 
34447                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34448                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34449                 
34450                 var targetWidth = this.minWidth - 2 * x;
34451                 var targetHeight = this.minHeight - 2 * y;
34452                 
34453                 var scale = 1;
34454                 
34455                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34456                     scale = targetWidth / width;
34457                 }
34458                 
34459                 if(x > 0 && y == 0){
34460                     scale = targetHeight / height;
34461                 }
34462                 
34463                 if(x > 0 && y > 0){
34464                     scale = targetWidth / width;
34465                     
34466                     if(width < height){
34467                         scale = targetHeight / height;
34468                     }
34469                 }
34470                 
34471                 context.scale(scale, scale);
34472                 
34473                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34474                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34475
34476                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34477                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34478                 
34479                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34480                 
34481                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34482                 
34483                 break;
34484             default : 
34485                 break;
34486         }
34487         
34488         this.cropData = canvas.toDataURL(this.cropType);
34489         
34490         if(this.fireEvent('crop', this, this.cropData) !== false){
34491             this.process(this.file, this.cropData);
34492         }
34493         
34494         return;
34495         
34496     },
34497     
34498     setThumbBoxSize : function()
34499     {
34500         var width, height;
34501         
34502         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34503             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34504             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34505             
34506             this.minWidth = width;
34507             this.minHeight = height;
34508             
34509             if(this.rotate == 90 || this.rotate == 270){
34510                 this.minWidth = height;
34511                 this.minHeight = width;
34512             }
34513         }
34514         
34515         height = 300;
34516         width = Math.ceil(this.minWidth * height / this.minHeight);
34517         
34518         if(this.minWidth > this.minHeight){
34519             width = 300;
34520             height = Math.ceil(this.minHeight * width / this.minWidth);
34521         }
34522         
34523         this.thumbEl.setStyle({
34524             width : width + 'px',
34525             height : height + 'px'
34526         });
34527
34528         return;
34529             
34530     },
34531     
34532     setThumbBoxPosition : function()
34533     {
34534         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34535         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34536         
34537         this.thumbEl.setLeft(x);
34538         this.thumbEl.setTop(y);
34539         
34540     },
34541     
34542     baseRotateLevel : function()
34543     {
34544         this.baseRotate = 1;
34545         
34546         if(
34547                 typeof(this.exif) != 'undefined' &&
34548                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34549                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34550         ){
34551             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34552         }
34553         
34554         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34555         
34556     },
34557     
34558     baseScaleLevel : function()
34559     {
34560         var width, height;
34561         
34562         if(this.isDocument){
34563             
34564             if(this.baseRotate == 6 || this.baseRotate == 8){
34565             
34566                 height = this.thumbEl.getHeight();
34567                 this.baseScale = height / this.imageEl.OriginWidth;
34568
34569                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34570                     width = this.thumbEl.getWidth();
34571                     this.baseScale = width / this.imageEl.OriginHeight;
34572                 }
34573
34574                 return;
34575             }
34576
34577             height = this.thumbEl.getHeight();
34578             this.baseScale = height / this.imageEl.OriginHeight;
34579
34580             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34581                 width = this.thumbEl.getWidth();
34582                 this.baseScale = width / this.imageEl.OriginWidth;
34583             }
34584
34585             return;
34586         }
34587         
34588         if(this.baseRotate == 6 || this.baseRotate == 8){
34589             
34590             width = this.thumbEl.getHeight();
34591             this.baseScale = width / this.imageEl.OriginHeight;
34592             
34593             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34594                 height = this.thumbEl.getWidth();
34595                 this.baseScale = height / this.imageEl.OriginHeight;
34596             }
34597             
34598             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34599                 height = this.thumbEl.getWidth();
34600                 this.baseScale = height / this.imageEl.OriginHeight;
34601                 
34602                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34603                     width = this.thumbEl.getHeight();
34604                     this.baseScale = width / this.imageEl.OriginWidth;
34605                 }
34606             }
34607             
34608             return;
34609         }
34610         
34611         width = this.thumbEl.getWidth();
34612         this.baseScale = width / this.imageEl.OriginWidth;
34613         
34614         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34615             height = this.thumbEl.getHeight();
34616             this.baseScale = height / this.imageEl.OriginHeight;
34617         }
34618         
34619         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34620             
34621             height = this.thumbEl.getHeight();
34622             this.baseScale = height / this.imageEl.OriginHeight;
34623             
34624             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34625                 width = this.thumbEl.getWidth();
34626                 this.baseScale = width / this.imageEl.OriginWidth;
34627             }
34628             
34629         }
34630         
34631         return;
34632     },
34633     
34634     getScaleLevel : function()
34635     {
34636         return this.baseScale * Math.pow(1.1, this.scale);
34637     },
34638     
34639     onTouchStart : function(e)
34640     {
34641         if(!this.canvasLoaded){
34642             this.beforeSelectFile(e);
34643             return;
34644         }
34645         
34646         var touches = e.browserEvent.touches;
34647         
34648         if(!touches){
34649             return;
34650         }
34651         
34652         if(touches.length == 1){
34653             this.onMouseDown(e);
34654             return;
34655         }
34656         
34657         if(touches.length != 2){
34658             return;
34659         }
34660         
34661         var coords = [];
34662         
34663         for(var i = 0, finger; finger = touches[i]; i++){
34664             coords.push(finger.pageX, finger.pageY);
34665         }
34666         
34667         var x = Math.pow(coords[0] - coords[2], 2);
34668         var y = Math.pow(coords[1] - coords[3], 2);
34669         
34670         this.startDistance = Math.sqrt(x + y);
34671         
34672         this.startScale = this.scale;
34673         
34674         this.pinching = true;
34675         this.dragable = false;
34676         
34677     },
34678     
34679     onTouchMove : function(e)
34680     {
34681         if(!this.pinching && !this.dragable){
34682             return;
34683         }
34684         
34685         var touches = e.browserEvent.touches;
34686         
34687         if(!touches){
34688             return;
34689         }
34690         
34691         if(this.dragable){
34692             this.onMouseMove(e);
34693             return;
34694         }
34695         
34696         var coords = [];
34697         
34698         for(var i = 0, finger; finger = touches[i]; i++){
34699             coords.push(finger.pageX, finger.pageY);
34700         }
34701         
34702         var x = Math.pow(coords[0] - coords[2], 2);
34703         var y = Math.pow(coords[1] - coords[3], 2);
34704         
34705         this.endDistance = Math.sqrt(x + y);
34706         
34707         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34708         
34709         if(!this.zoomable()){
34710             this.scale = this.startScale;
34711             return;
34712         }
34713         
34714         this.draw();
34715         
34716     },
34717     
34718     onTouchEnd : function(e)
34719     {
34720         this.pinching = false;
34721         this.dragable = false;
34722         
34723     },
34724     
34725     process : function(file, crop)
34726     {
34727         if(this.loadMask){
34728             this.maskEl.mask(this.loadingText);
34729         }
34730         
34731         this.xhr = new XMLHttpRequest();
34732         
34733         file.xhr = this.xhr;
34734
34735         this.xhr.open(this.method, this.url, true);
34736         
34737         var headers = {
34738             "Accept": "application/json",
34739             "Cache-Control": "no-cache",
34740             "X-Requested-With": "XMLHttpRequest"
34741         };
34742         
34743         for (var headerName in headers) {
34744             var headerValue = headers[headerName];
34745             if (headerValue) {
34746                 this.xhr.setRequestHeader(headerName, headerValue);
34747             }
34748         }
34749         
34750         var _this = this;
34751         
34752         this.xhr.onload = function()
34753         {
34754             _this.xhrOnLoad(_this.xhr);
34755         }
34756         
34757         this.xhr.onerror = function()
34758         {
34759             _this.xhrOnError(_this.xhr);
34760         }
34761         
34762         var formData = new FormData();
34763
34764         formData.append('returnHTML', 'NO');
34765         
34766         if(crop){
34767             formData.append('crop', crop);
34768         }
34769         
34770         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34771             formData.append(this.paramName, file, file.name);
34772         }
34773         
34774         if(typeof(file.filename) != 'undefined'){
34775             formData.append('filename', file.filename);
34776         }
34777         
34778         if(typeof(file.mimetype) != 'undefined'){
34779             formData.append('mimetype', file.mimetype);
34780         }
34781         
34782         if(this.fireEvent('arrange', this, formData) != false){
34783             this.xhr.send(formData);
34784         };
34785     },
34786     
34787     xhrOnLoad : function(xhr)
34788     {
34789         if(this.loadMask){
34790             this.maskEl.unmask();
34791         }
34792         
34793         if (xhr.readyState !== 4) {
34794             this.fireEvent('exception', this, xhr);
34795             return;
34796         }
34797
34798         var response = Roo.decode(xhr.responseText);
34799         
34800         if(!response.success){
34801             this.fireEvent('exception', this, xhr);
34802             return;
34803         }
34804         
34805         var response = Roo.decode(xhr.responseText);
34806         
34807         this.fireEvent('upload', this, response);
34808         
34809     },
34810     
34811     xhrOnError : function()
34812     {
34813         if(this.loadMask){
34814             this.maskEl.unmask();
34815         }
34816         
34817         Roo.log('xhr on error');
34818         
34819         var response = Roo.decode(xhr.responseText);
34820           
34821         Roo.log(response);
34822         
34823     },
34824     
34825     prepare : function(file)
34826     {   
34827         if(this.loadMask){
34828             this.maskEl.mask(this.loadingText);
34829         }
34830         
34831         this.file = false;
34832         this.exif = {};
34833         
34834         if(typeof(file) === 'string'){
34835             this.loadCanvas(file);
34836             return;
34837         }
34838         
34839         if(!file || !this.urlAPI){
34840             return;
34841         }
34842         
34843         this.file = file;
34844         this.cropType = file.type;
34845         
34846         var _this = this;
34847         
34848         if(this.fireEvent('prepare', this, this.file) != false){
34849             
34850             var reader = new FileReader();
34851             
34852             reader.onload = function (e) {
34853                 if (e.target.error) {
34854                     Roo.log(e.target.error);
34855                     return;
34856                 }
34857                 
34858                 var buffer = e.target.result,
34859                     dataView = new DataView(buffer),
34860                     offset = 2,
34861                     maxOffset = dataView.byteLength - 4,
34862                     markerBytes,
34863                     markerLength;
34864                 
34865                 if (dataView.getUint16(0) === 0xffd8) {
34866                     while (offset < maxOffset) {
34867                         markerBytes = dataView.getUint16(offset);
34868                         
34869                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34870                             markerLength = dataView.getUint16(offset + 2) + 2;
34871                             if (offset + markerLength > dataView.byteLength) {
34872                                 Roo.log('Invalid meta data: Invalid segment size.');
34873                                 break;
34874                             }
34875                             
34876                             if(markerBytes == 0xffe1){
34877                                 _this.parseExifData(
34878                                     dataView,
34879                                     offset,
34880                                     markerLength
34881                                 );
34882                             }
34883                             
34884                             offset += markerLength;
34885                             
34886                             continue;
34887                         }
34888                         
34889                         break;
34890                     }
34891                     
34892                 }
34893                 
34894                 var url = _this.urlAPI.createObjectURL(_this.file);
34895                 
34896                 _this.loadCanvas(url);
34897                 
34898                 return;
34899             }
34900             
34901             reader.readAsArrayBuffer(this.file);
34902             
34903         }
34904         
34905     },
34906     
34907     parseExifData : function(dataView, offset, length)
34908     {
34909         var tiffOffset = offset + 10,
34910             littleEndian,
34911             dirOffset;
34912     
34913         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34914             // No Exif data, might be XMP data instead
34915             return;
34916         }
34917         
34918         // Check for the ASCII code for "Exif" (0x45786966):
34919         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34920             // No Exif data, might be XMP data instead
34921             return;
34922         }
34923         if (tiffOffset + 8 > dataView.byteLength) {
34924             Roo.log('Invalid Exif data: Invalid segment size.');
34925             return;
34926         }
34927         // Check for the two null bytes:
34928         if (dataView.getUint16(offset + 8) !== 0x0000) {
34929             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34930             return;
34931         }
34932         // Check the byte alignment:
34933         switch (dataView.getUint16(tiffOffset)) {
34934         case 0x4949:
34935             littleEndian = true;
34936             break;
34937         case 0x4D4D:
34938             littleEndian = false;
34939             break;
34940         default:
34941             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34942             return;
34943         }
34944         // Check for the TIFF tag marker (0x002A):
34945         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34946             Roo.log('Invalid Exif data: Missing TIFF marker.');
34947             return;
34948         }
34949         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34950         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34951         
34952         this.parseExifTags(
34953             dataView,
34954             tiffOffset,
34955             tiffOffset + dirOffset,
34956             littleEndian
34957         );
34958     },
34959     
34960     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34961     {
34962         var tagsNumber,
34963             dirEndOffset,
34964             i;
34965         if (dirOffset + 6 > dataView.byteLength) {
34966             Roo.log('Invalid Exif data: Invalid directory offset.');
34967             return;
34968         }
34969         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34970         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34971         if (dirEndOffset + 4 > dataView.byteLength) {
34972             Roo.log('Invalid Exif data: Invalid directory size.');
34973             return;
34974         }
34975         for (i = 0; i < tagsNumber; i += 1) {
34976             this.parseExifTag(
34977                 dataView,
34978                 tiffOffset,
34979                 dirOffset + 2 + 12 * i, // tag offset
34980                 littleEndian
34981             );
34982         }
34983         // Return the offset to the next directory:
34984         return dataView.getUint32(dirEndOffset, littleEndian);
34985     },
34986     
34987     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34988     {
34989         var tag = dataView.getUint16(offset, littleEndian);
34990         
34991         this.exif[tag] = this.getExifValue(
34992             dataView,
34993             tiffOffset,
34994             offset,
34995             dataView.getUint16(offset + 2, littleEndian), // tag type
34996             dataView.getUint32(offset + 4, littleEndian), // tag length
34997             littleEndian
34998         );
34999     },
35000     
35001     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35002     {
35003         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35004             tagSize,
35005             dataOffset,
35006             values,
35007             i,
35008             str,
35009             c;
35010     
35011         if (!tagType) {
35012             Roo.log('Invalid Exif data: Invalid tag type.');
35013             return;
35014         }
35015         
35016         tagSize = tagType.size * length;
35017         // Determine if the value is contained in the dataOffset bytes,
35018         // or if the value at the dataOffset is a pointer to the actual data:
35019         dataOffset = tagSize > 4 ?
35020                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35021         if (dataOffset + tagSize > dataView.byteLength) {
35022             Roo.log('Invalid Exif data: Invalid data offset.');
35023             return;
35024         }
35025         if (length === 1) {
35026             return tagType.getValue(dataView, dataOffset, littleEndian);
35027         }
35028         values = [];
35029         for (i = 0; i < length; i += 1) {
35030             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35031         }
35032         
35033         if (tagType.ascii) {
35034             str = '';
35035             // Concatenate the chars:
35036             for (i = 0; i < values.length; i += 1) {
35037                 c = values[i];
35038                 // Ignore the terminating NULL byte(s):
35039                 if (c === '\u0000') {
35040                     break;
35041                 }
35042                 str += c;
35043             }
35044             return str;
35045         }
35046         return values;
35047     }
35048     
35049 });
35050
35051 Roo.apply(Roo.bootstrap.UploadCropbox, {
35052     tags : {
35053         'Orientation': 0x0112
35054     },
35055     
35056     Orientation: {
35057             1: 0, //'top-left',
35058 //            2: 'top-right',
35059             3: 180, //'bottom-right',
35060 //            4: 'bottom-left',
35061 //            5: 'left-top',
35062             6: 90, //'right-top',
35063 //            7: 'right-bottom',
35064             8: 270 //'left-bottom'
35065     },
35066     
35067     exifTagTypes : {
35068         // byte, 8-bit unsigned int:
35069         1: {
35070             getValue: function (dataView, dataOffset) {
35071                 return dataView.getUint8(dataOffset);
35072             },
35073             size: 1
35074         },
35075         // ascii, 8-bit byte:
35076         2: {
35077             getValue: function (dataView, dataOffset) {
35078                 return String.fromCharCode(dataView.getUint8(dataOffset));
35079             },
35080             size: 1,
35081             ascii: true
35082         },
35083         // short, 16 bit int:
35084         3: {
35085             getValue: function (dataView, dataOffset, littleEndian) {
35086                 return dataView.getUint16(dataOffset, littleEndian);
35087             },
35088             size: 2
35089         },
35090         // long, 32 bit int:
35091         4: {
35092             getValue: function (dataView, dataOffset, littleEndian) {
35093                 return dataView.getUint32(dataOffset, littleEndian);
35094             },
35095             size: 4
35096         },
35097         // rational = two long values, first is numerator, second is denominator:
35098         5: {
35099             getValue: function (dataView, dataOffset, littleEndian) {
35100                 return dataView.getUint32(dataOffset, littleEndian) /
35101                     dataView.getUint32(dataOffset + 4, littleEndian);
35102             },
35103             size: 8
35104         },
35105         // slong, 32 bit signed int:
35106         9: {
35107             getValue: function (dataView, dataOffset, littleEndian) {
35108                 return dataView.getInt32(dataOffset, littleEndian);
35109             },
35110             size: 4
35111         },
35112         // srational, two slongs, first is numerator, second is denominator:
35113         10: {
35114             getValue: function (dataView, dataOffset, littleEndian) {
35115                 return dataView.getInt32(dataOffset, littleEndian) /
35116                     dataView.getInt32(dataOffset + 4, littleEndian);
35117             },
35118             size: 8
35119         }
35120     },
35121     
35122     footer : {
35123         STANDARD : [
35124             {
35125                 tag : 'div',
35126                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35127                 action : 'rotate-left',
35128                 cn : [
35129                     {
35130                         tag : 'button',
35131                         cls : 'btn btn-default',
35132                         html : '<i class="fa fa-undo"></i>'
35133                     }
35134                 ]
35135             },
35136             {
35137                 tag : 'div',
35138                 cls : 'btn-group roo-upload-cropbox-picture',
35139                 action : 'picture',
35140                 cn : [
35141                     {
35142                         tag : 'button',
35143                         cls : 'btn btn-default',
35144                         html : '<i class="fa fa-picture-o"></i>'
35145                     }
35146                 ]
35147             },
35148             {
35149                 tag : 'div',
35150                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35151                 action : 'rotate-right',
35152                 cn : [
35153                     {
35154                         tag : 'button',
35155                         cls : 'btn btn-default',
35156                         html : '<i class="fa fa-repeat"></i>'
35157                     }
35158                 ]
35159             }
35160         ],
35161         DOCUMENT : [
35162             {
35163                 tag : 'div',
35164                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35165                 action : 'rotate-left',
35166                 cn : [
35167                     {
35168                         tag : 'button',
35169                         cls : 'btn btn-default',
35170                         html : '<i class="fa fa-undo"></i>'
35171                     }
35172                 ]
35173             },
35174             {
35175                 tag : 'div',
35176                 cls : 'btn-group roo-upload-cropbox-download',
35177                 action : 'download',
35178                 cn : [
35179                     {
35180                         tag : 'button',
35181                         cls : 'btn btn-default',
35182                         html : '<i class="fa fa-download"></i>'
35183                     }
35184                 ]
35185             },
35186             {
35187                 tag : 'div',
35188                 cls : 'btn-group roo-upload-cropbox-crop',
35189                 action : 'crop',
35190                 cn : [
35191                     {
35192                         tag : 'button',
35193                         cls : 'btn btn-default',
35194                         html : '<i class="fa fa-crop"></i>'
35195                     }
35196                 ]
35197             },
35198             {
35199                 tag : 'div',
35200                 cls : 'btn-group roo-upload-cropbox-trash',
35201                 action : 'trash',
35202                 cn : [
35203                     {
35204                         tag : 'button',
35205                         cls : 'btn btn-default',
35206                         html : '<i class="fa fa-trash"></i>'
35207                     }
35208                 ]
35209             },
35210             {
35211                 tag : 'div',
35212                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35213                 action : 'rotate-right',
35214                 cn : [
35215                     {
35216                         tag : 'button',
35217                         cls : 'btn btn-default',
35218                         html : '<i class="fa fa-repeat"></i>'
35219                     }
35220                 ]
35221             }
35222         ],
35223         ROTATOR : [
35224             {
35225                 tag : 'div',
35226                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35227                 action : 'rotate-left',
35228                 cn : [
35229                     {
35230                         tag : 'button',
35231                         cls : 'btn btn-default',
35232                         html : '<i class="fa fa-undo"></i>'
35233                     }
35234                 ]
35235             },
35236             {
35237                 tag : 'div',
35238                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35239                 action : 'rotate-right',
35240                 cn : [
35241                     {
35242                         tag : 'button',
35243                         cls : 'btn btn-default',
35244                         html : '<i class="fa fa-repeat"></i>'
35245                     }
35246                 ]
35247             }
35248         ]
35249     }
35250 });
35251
35252 /*
35253 * Licence: LGPL
35254 */
35255
35256 /**
35257  * @class Roo.bootstrap.DocumentManager
35258  * @extends Roo.bootstrap.Component
35259  * Bootstrap DocumentManager class
35260  * @cfg {String} paramName default 'imageUpload'
35261  * @cfg {String} toolTipName default 'filename'
35262  * @cfg {String} method default POST
35263  * @cfg {String} url action url
35264  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35265  * @cfg {Boolean} multiple multiple upload default true
35266  * @cfg {Number} thumbSize default 300
35267  * @cfg {String} fieldLabel
35268  * @cfg {Number} labelWidth default 4
35269  * @cfg {String} labelAlign (left|top) default left
35270  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35271 * @cfg {Number} labellg set the width of label (1-12)
35272  * @cfg {Number} labelmd set the width of label (1-12)
35273  * @cfg {Number} labelsm set the width of label (1-12)
35274  * @cfg {Number} labelxs set the width of label (1-12)
35275  * 
35276  * @constructor
35277  * Create a new DocumentManager
35278  * @param {Object} config The config object
35279  */
35280
35281 Roo.bootstrap.DocumentManager = function(config){
35282     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35283     
35284     this.files = [];
35285     this.delegates = [];
35286     
35287     this.addEvents({
35288         /**
35289          * @event initial
35290          * Fire when initial the DocumentManager
35291          * @param {Roo.bootstrap.DocumentManager} this
35292          */
35293         "initial" : true,
35294         /**
35295          * @event inspect
35296          * inspect selected file
35297          * @param {Roo.bootstrap.DocumentManager} this
35298          * @param {File} file
35299          */
35300         "inspect" : true,
35301         /**
35302          * @event exception
35303          * Fire when xhr load exception
35304          * @param {Roo.bootstrap.DocumentManager} this
35305          * @param {XMLHttpRequest} xhr
35306          */
35307         "exception" : true,
35308         /**
35309          * @event afterupload
35310          * Fire when xhr load exception
35311          * @param {Roo.bootstrap.DocumentManager} this
35312          * @param {XMLHttpRequest} xhr
35313          */
35314         "afterupload" : true,
35315         /**
35316          * @event prepare
35317          * prepare the form data
35318          * @param {Roo.bootstrap.DocumentManager} this
35319          * @param {Object} formData
35320          */
35321         "prepare" : true,
35322         /**
35323          * @event remove
35324          * Fire when remove the file
35325          * @param {Roo.bootstrap.DocumentManager} this
35326          * @param {Object} file
35327          */
35328         "remove" : true,
35329         /**
35330          * @event refresh
35331          * Fire after refresh the file
35332          * @param {Roo.bootstrap.DocumentManager} this
35333          */
35334         "refresh" : true,
35335         /**
35336          * @event click
35337          * Fire after click the image
35338          * @param {Roo.bootstrap.DocumentManager} this
35339          * @param {Object} file
35340          */
35341         "click" : true,
35342         /**
35343          * @event edit
35344          * Fire when upload a image and editable set to true
35345          * @param {Roo.bootstrap.DocumentManager} this
35346          * @param {Object} file
35347          */
35348         "edit" : true,
35349         /**
35350          * @event beforeselectfile
35351          * Fire before select file
35352          * @param {Roo.bootstrap.DocumentManager} this
35353          */
35354         "beforeselectfile" : true,
35355         /**
35356          * @event process
35357          * Fire before process file
35358          * @param {Roo.bootstrap.DocumentManager} this
35359          * @param {Object} file
35360          */
35361         "process" : true,
35362         /**
35363          * @event previewrendered
35364          * Fire when preview rendered
35365          * @param {Roo.bootstrap.DocumentManager} this
35366          * @param {Object} file
35367          */
35368         "previewrendered" : true,
35369         /**
35370          */
35371         "previewResize" : true
35372         
35373     });
35374 };
35375
35376 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35377     
35378     boxes : 0,
35379     inputName : '',
35380     thumbSize : 300,
35381     multiple : true,
35382     files : false,
35383     method : 'POST',
35384     url : '',
35385     paramName : 'imageUpload',
35386     toolTipName : 'filename',
35387     fieldLabel : '',
35388     labelWidth : 4,
35389     labelAlign : 'left',
35390     editable : true,
35391     delegates : false,
35392     xhr : false, 
35393     
35394     labellg : 0,
35395     labelmd : 0,
35396     labelsm : 0,
35397     labelxs : 0,
35398     
35399     getAutoCreate : function()
35400     {   
35401         var managerWidget = {
35402             tag : 'div',
35403             cls : 'roo-document-manager',
35404             cn : [
35405                 {
35406                     tag : 'input',
35407                     cls : 'roo-document-manager-selector',
35408                     type : 'file'
35409                 },
35410                 {
35411                     tag : 'div',
35412                     cls : 'roo-document-manager-uploader',
35413                     cn : [
35414                         {
35415                             tag : 'div',
35416                             cls : 'roo-document-manager-upload-btn',
35417                             html : '<i class="fa fa-plus"></i>'
35418                         }
35419                     ]
35420                     
35421                 }
35422             ]
35423         };
35424         
35425         var content = [
35426             {
35427                 tag : 'div',
35428                 cls : 'column col-md-12',
35429                 cn : managerWidget
35430             }
35431         ];
35432         
35433         if(this.fieldLabel.length){
35434             
35435             content = [
35436                 {
35437                     tag : 'div',
35438                     cls : 'column col-md-12',
35439                     html : this.fieldLabel
35440                 },
35441                 {
35442                     tag : 'div',
35443                     cls : 'column col-md-12',
35444                     cn : managerWidget
35445                 }
35446             ];
35447
35448             if(this.labelAlign == 'left'){
35449                 content = [
35450                     {
35451                         tag : 'div',
35452                         cls : 'column',
35453                         html : this.fieldLabel
35454                     },
35455                     {
35456                         tag : 'div',
35457                         cls : 'column',
35458                         cn : managerWidget
35459                     }
35460                 ];
35461                 
35462                 if(this.labelWidth > 12){
35463                     content[0].style = "width: " + this.labelWidth + 'px';
35464                 }
35465
35466                 if(this.labelWidth < 13 && this.labelmd == 0){
35467                     this.labelmd = this.labelWidth;
35468                 }
35469
35470                 if(this.labellg > 0){
35471                     content[0].cls += ' col-lg-' + this.labellg;
35472                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35473                 }
35474
35475                 if(this.labelmd > 0){
35476                     content[0].cls += ' col-md-' + this.labelmd;
35477                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35478                 }
35479
35480                 if(this.labelsm > 0){
35481                     content[0].cls += ' col-sm-' + this.labelsm;
35482                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35483                 }
35484
35485                 if(this.labelxs > 0){
35486                     content[0].cls += ' col-xs-' + this.labelxs;
35487                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35488                 }
35489                 
35490             }
35491         }
35492         
35493         var cfg = {
35494             tag : 'div',
35495             cls : 'row clearfix',
35496             cn : content
35497         };
35498         
35499         return cfg;
35500         
35501     },
35502     
35503     initEvents : function()
35504     {
35505         this.managerEl = this.el.select('.roo-document-manager', true).first();
35506         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35507         
35508         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35509         this.selectorEl.hide();
35510         
35511         if(this.multiple){
35512             this.selectorEl.attr('multiple', 'multiple');
35513         }
35514         
35515         this.selectorEl.on('change', this.onFileSelected, this);
35516         
35517         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35518         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35519         
35520         this.uploader.on('click', this.onUploaderClick, this);
35521         
35522         this.renderProgressDialog();
35523         
35524         var _this = this;
35525         
35526         window.addEventListener("resize", function() { _this.refresh(); } );
35527         
35528         this.fireEvent('initial', this);
35529     },
35530     
35531     renderProgressDialog : function()
35532     {
35533         var _this = this;
35534         
35535         this.progressDialog = new Roo.bootstrap.Modal({
35536             cls : 'roo-document-manager-progress-dialog',
35537             allow_close : false,
35538             animate : false,
35539             title : '',
35540             buttons : [
35541                 {
35542                     name  :'cancel',
35543                     weight : 'danger',
35544                     html : 'Cancel'
35545                 }
35546             ], 
35547             listeners : { 
35548                 btnclick : function() {
35549                     _this.uploadCancel();
35550                     this.hide();
35551                 }
35552             }
35553         });
35554          
35555         this.progressDialog.render(Roo.get(document.body));
35556          
35557         this.progress = new Roo.bootstrap.Progress({
35558             cls : 'roo-document-manager-progress',
35559             active : true,
35560             striped : true
35561         });
35562         
35563         this.progress.render(this.progressDialog.getChildContainer());
35564         
35565         this.progressBar = new Roo.bootstrap.ProgressBar({
35566             cls : 'roo-document-manager-progress-bar',
35567             aria_valuenow : 0,
35568             aria_valuemin : 0,
35569             aria_valuemax : 12,
35570             panel : 'success'
35571         });
35572         
35573         this.progressBar.render(this.progress.getChildContainer());
35574     },
35575     
35576     onUploaderClick : function(e)
35577     {
35578         e.preventDefault();
35579      
35580         if(this.fireEvent('beforeselectfile', this) != false){
35581             this.selectorEl.dom.click();
35582         }
35583         
35584     },
35585     
35586     onFileSelected : function(e)
35587     {
35588         e.preventDefault();
35589         
35590         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35591             return;
35592         }
35593         
35594         Roo.each(this.selectorEl.dom.files, function(file){
35595             if(this.fireEvent('inspect', this, file) != false){
35596                 this.files.push(file);
35597             }
35598         }, this);
35599         
35600         this.queue();
35601         
35602     },
35603     
35604     queue : function()
35605     {
35606         this.selectorEl.dom.value = '';
35607         
35608         if(!this.files || !this.files.length){
35609             return;
35610         }
35611         
35612         if(this.boxes > 0 && this.files.length > this.boxes){
35613             this.files = this.files.slice(0, this.boxes);
35614         }
35615         
35616         this.uploader.show();
35617         
35618         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35619             this.uploader.hide();
35620         }
35621         
35622         var _this = this;
35623         
35624         var files = [];
35625         
35626         var docs = [];
35627         
35628         Roo.each(this.files, function(file){
35629             
35630             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35631                 var f = this.renderPreview(file);
35632                 files.push(f);
35633                 return;
35634             }
35635             
35636             if(file.type.indexOf('image') != -1){
35637                 this.delegates.push(
35638                     (function(){
35639                         _this.process(file);
35640                     }).createDelegate(this)
35641                 );
35642         
35643                 return;
35644             }
35645             
35646             docs.push(
35647                 (function(){
35648                     _this.process(file);
35649                 }).createDelegate(this)
35650             );
35651             
35652         }, this);
35653         
35654         this.files = files;
35655         
35656         this.delegates = this.delegates.concat(docs);
35657         
35658         if(!this.delegates.length){
35659             this.refresh();
35660             return;
35661         }
35662         
35663         this.progressBar.aria_valuemax = this.delegates.length;
35664         
35665         this.arrange();
35666         
35667         return;
35668     },
35669     
35670     arrange : function()
35671     {
35672         if(!this.delegates.length){
35673             this.progressDialog.hide();
35674             this.refresh();
35675             return;
35676         }
35677         
35678         var delegate = this.delegates.shift();
35679         
35680         this.progressDialog.show();
35681         
35682         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35683         
35684         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35685         
35686         delegate();
35687     },
35688     
35689     refresh : function()
35690     {
35691         this.uploader.show();
35692         
35693         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35694             this.uploader.hide();
35695         }
35696         
35697         Roo.isTouch ? this.closable(false) : this.closable(true);
35698         
35699         this.fireEvent('refresh', this);
35700     },
35701     
35702     onRemove : function(e, el, o)
35703     {
35704         e.preventDefault();
35705         
35706         this.fireEvent('remove', this, o);
35707         
35708     },
35709     
35710     remove : function(o)
35711     {
35712         var files = [];
35713         
35714         Roo.each(this.files, function(file){
35715             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35716                 files.push(file);
35717                 return;
35718             }
35719
35720             o.target.remove();
35721
35722         }, this);
35723         
35724         this.files = files;
35725         
35726         this.refresh();
35727     },
35728     
35729     clear : function()
35730     {
35731         Roo.each(this.files, function(file){
35732             if(!file.target){
35733                 return;
35734             }
35735             
35736             file.target.remove();
35737
35738         }, this);
35739         
35740         this.files = [];
35741         
35742         this.refresh();
35743     },
35744     
35745     onClick : function(e, el, o)
35746     {
35747         e.preventDefault();
35748         
35749         this.fireEvent('click', this, o);
35750         
35751     },
35752     
35753     closable : function(closable)
35754     {
35755         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35756             
35757             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35758             
35759             if(closable){
35760                 el.show();
35761                 return;
35762             }
35763             
35764             el.hide();
35765             
35766         }, this);
35767     },
35768     
35769     xhrOnLoad : function(xhr)
35770     {
35771         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35772             el.remove();
35773         }, this);
35774         
35775         if (xhr.readyState !== 4) {
35776             this.arrange();
35777             this.fireEvent('exception', this, xhr);
35778             return;
35779         }
35780
35781         var response = Roo.decode(xhr.responseText);
35782         
35783         if(!response.success){
35784             this.arrange();
35785             this.fireEvent('exception', this, xhr);
35786             return;
35787         }
35788         
35789         var file = this.renderPreview(response.data);
35790         
35791         this.files.push(file);
35792         
35793         this.arrange();
35794         
35795         this.fireEvent('afterupload', this, xhr);
35796         
35797     },
35798     
35799     xhrOnError : function(xhr)
35800     {
35801         Roo.log('xhr on error');
35802         
35803         var response = Roo.decode(xhr.responseText);
35804           
35805         Roo.log(response);
35806         
35807         this.arrange();
35808     },
35809     
35810     process : function(file)
35811     {
35812         if(this.fireEvent('process', this, file) !== false){
35813             if(this.editable && file.type.indexOf('image') != -1){
35814                 this.fireEvent('edit', this, file);
35815                 return;
35816             }
35817
35818             this.uploadStart(file, false);
35819
35820             return;
35821         }
35822         
35823     },
35824     
35825     uploadStart : function(file, crop)
35826     {
35827         this.xhr = new XMLHttpRequest();
35828         
35829         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35830             this.arrange();
35831             return;
35832         }
35833         
35834         file.xhr = this.xhr;
35835             
35836         this.managerEl.createChild({
35837             tag : 'div',
35838             cls : 'roo-document-manager-loading',
35839             cn : [
35840                 {
35841                     tag : 'div',
35842                     tooltip : file.name,
35843                     cls : 'roo-document-manager-thumb',
35844                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35845                 }
35846             ]
35847
35848         });
35849
35850         this.xhr.open(this.method, this.url, true);
35851         
35852         var headers = {
35853             "Accept": "application/json",
35854             "Cache-Control": "no-cache",
35855             "X-Requested-With": "XMLHttpRequest"
35856         };
35857         
35858         for (var headerName in headers) {
35859             var headerValue = headers[headerName];
35860             if (headerValue) {
35861                 this.xhr.setRequestHeader(headerName, headerValue);
35862             }
35863         }
35864         
35865         var _this = this;
35866         
35867         this.xhr.onload = function()
35868         {
35869             _this.xhrOnLoad(_this.xhr);
35870         }
35871         
35872         this.xhr.onerror = function()
35873         {
35874             _this.xhrOnError(_this.xhr);
35875         }
35876         
35877         var formData = new FormData();
35878
35879         formData.append('returnHTML', 'NO');
35880         
35881         if(crop){
35882             formData.append('crop', crop);
35883         }
35884         
35885         formData.append(this.paramName, file, file.name);
35886         
35887         var options = {
35888             file : file, 
35889             manually : false
35890         };
35891         
35892         if(this.fireEvent('prepare', this, formData, options) != false){
35893             
35894             if(options.manually){
35895                 return;
35896             }
35897             
35898             this.xhr.send(formData);
35899             return;
35900         };
35901         
35902         this.uploadCancel();
35903     },
35904     
35905     uploadCancel : function()
35906     {
35907         if (this.xhr) {
35908             this.xhr.abort();
35909         }
35910         
35911         this.delegates = [];
35912         
35913         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35914             el.remove();
35915         }, this);
35916         
35917         this.arrange();
35918     },
35919     
35920     renderPreview : function(file)
35921     {
35922         if(typeof(file.target) != 'undefined' && file.target){
35923             return file;
35924         }
35925         
35926         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35927         
35928         var previewEl = this.managerEl.createChild({
35929             tag : 'div',
35930             cls : 'roo-document-manager-preview',
35931             cn : [
35932                 {
35933                     tag : 'div',
35934                     tooltip : file[this.toolTipName],
35935                     cls : 'roo-document-manager-thumb',
35936                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35937                 },
35938                 {
35939                     tag : 'button',
35940                     cls : 'close',
35941                     html : '<i class="fa fa-times-circle"></i>'
35942                 }
35943             ]
35944         });
35945
35946         var close = previewEl.select('button.close', true).first();
35947
35948         close.on('click', this.onRemove, this, file);
35949
35950         file.target = previewEl;
35951
35952         var image = previewEl.select('img', true).first();
35953         
35954         var _this = this;
35955         
35956         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35957         
35958         image.on('click', this.onClick, this, file);
35959         
35960         this.fireEvent('previewrendered', this, file);
35961         
35962         return file;
35963         
35964     },
35965     
35966     onPreviewLoad : function(file, image)
35967     {
35968         if(typeof(file.target) == 'undefined' || !file.target){
35969             return;
35970         }
35971         
35972         var width = image.dom.naturalWidth || image.dom.width;
35973         var height = image.dom.naturalHeight || image.dom.height;
35974         
35975         if(!this.previewResize) {
35976             return;
35977         }
35978         
35979         if(width > height){
35980             file.target.addClass('wide');
35981             return;
35982         }
35983         
35984         file.target.addClass('tall');
35985         return;
35986         
35987     },
35988     
35989     uploadFromSource : function(file, crop)
35990     {
35991         this.xhr = new XMLHttpRequest();
35992         
35993         this.managerEl.createChild({
35994             tag : 'div',
35995             cls : 'roo-document-manager-loading',
35996             cn : [
35997                 {
35998                     tag : 'div',
35999                     tooltip : file.name,
36000                     cls : 'roo-document-manager-thumb',
36001                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36002                 }
36003             ]
36004
36005         });
36006
36007         this.xhr.open(this.method, this.url, true);
36008         
36009         var headers = {
36010             "Accept": "application/json",
36011             "Cache-Control": "no-cache",
36012             "X-Requested-With": "XMLHttpRequest"
36013         };
36014         
36015         for (var headerName in headers) {
36016             var headerValue = headers[headerName];
36017             if (headerValue) {
36018                 this.xhr.setRequestHeader(headerName, headerValue);
36019             }
36020         }
36021         
36022         var _this = this;
36023         
36024         this.xhr.onload = function()
36025         {
36026             _this.xhrOnLoad(_this.xhr);
36027         }
36028         
36029         this.xhr.onerror = function()
36030         {
36031             _this.xhrOnError(_this.xhr);
36032         }
36033         
36034         var formData = new FormData();
36035
36036         formData.append('returnHTML', 'NO');
36037         
36038         formData.append('crop', crop);
36039         
36040         if(typeof(file.filename) != 'undefined'){
36041             formData.append('filename', file.filename);
36042         }
36043         
36044         if(typeof(file.mimetype) != 'undefined'){
36045             formData.append('mimetype', file.mimetype);
36046         }
36047         
36048         Roo.log(formData);
36049         
36050         if(this.fireEvent('prepare', this, formData) != false){
36051             this.xhr.send(formData);
36052         };
36053     }
36054 });
36055
36056 /*
36057 * Licence: LGPL
36058 */
36059
36060 /**
36061  * @class Roo.bootstrap.DocumentViewer
36062  * @extends Roo.bootstrap.Component
36063  * Bootstrap DocumentViewer class
36064  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36065  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36066  * 
36067  * @constructor
36068  * Create a new DocumentViewer
36069  * @param {Object} config The config object
36070  */
36071
36072 Roo.bootstrap.DocumentViewer = function(config){
36073     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36074     
36075     this.addEvents({
36076         /**
36077          * @event initial
36078          * Fire after initEvent
36079          * @param {Roo.bootstrap.DocumentViewer} this
36080          */
36081         "initial" : true,
36082         /**
36083          * @event click
36084          * Fire after click
36085          * @param {Roo.bootstrap.DocumentViewer} this
36086          */
36087         "click" : true,
36088         /**
36089          * @event download
36090          * Fire after download button
36091          * @param {Roo.bootstrap.DocumentViewer} this
36092          */
36093         "download" : true,
36094         /**
36095          * @event trash
36096          * Fire after trash button
36097          * @param {Roo.bootstrap.DocumentViewer} this
36098          */
36099         "trash" : true
36100         
36101     });
36102 };
36103
36104 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36105     
36106     showDownload : true,
36107     
36108     showTrash : true,
36109     
36110     getAutoCreate : function()
36111     {
36112         var cfg = {
36113             tag : 'div',
36114             cls : 'roo-document-viewer',
36115             cn : [
36116                 {
36117                     tag : 'div',
36118                     cls : 'roo-document-viewer-body',
36119                     cn : [
36120                         {
36121                             tag : 'div',
36122                             cls : 'roo-document-viewer-thumb',
36123                             cn : [
36124                                 {
36125                                     tag : 'img',
36126                                     cls : 'roo-document-viewer-image'
36127                                 }
36128                             ]
36129                         }
36130                     ]
36131                 },
36132                 {
36133                     tag : 'div',
36134                     cls : 'roo-document-viewer-footer',
36135                     cn : {
36136                         tag : 'div',
36137                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36138                         cn : [
36139                             {
36140                                 tag : 'div',
36141                                 cls : 'btn-group roo-document-viewer-download',
36142                                 cn : [
36143                                     {
36144                                         tag : 'button',
36145                                         cls : 'btn btn-default',
36146                                         html : '<i class="fa fa-download"></i>'
36147                                     }
36148                                 ]
36149                             },
36150                             {
36151                                 tag : 'div',
36152                                 cls : 'btn-group roo-document-viewer-trash',
36153                                 cn : [
36154                                     {
36155                                         tag : 'button',
36156                                         cls : 'btn btn-default',
36157                                         html : '<i class="fa fa-trash"></i>'
36158                                     }
36159                                 ]
36160                             }
36161                         ]
36162                     }
36163                 }
36164             ]
36165         };
36166         
36167         return cfg;
36168     },
36169     
36170     initEvents : function()
36171     {
36172         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36173         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36174         
36175         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36176         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36177         
36178         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36179         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36180         
36181         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36182         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36183         
36184         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36185         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36186         
36187         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36188         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36189         
36190         this.bodyEl.on('click', this.onClick, this);
36191         this.downloadBtn.on('click', this.onDownload, this);
36192         this.trashBtn.on('click', this.onTrash, this);
36193         
36194         this.downloadBtn.hide();
36195         this.trashBtn.hide();
36196         
36197         if(this.showDownload){
36198             this.downloadBtn.show();
36199         }
36200         
36201         if(this.showTrash){
36202             this.trashBtn.show();
36203         }
36204         
36205         if(!this.showDownload && !this.showTrash) {
36206             this.footerEl.hide();
36207         }
36208         
36209     },
36210     
36211     initial : function()
36212     {
36213         this.fireEvent('initial', this);
36214         
36215     },
36216     
36217     onClick : function(e)
36218     {
36219         e.preventDefault();
36220         
36221         this.fireEvent('click', this);
36222     },
36223     
36224     onDownload : function(e)
36225     {
36226         e.preventDefault();
36227         
36228         this.fireEvent('download', this);
36229     },
36230     
36231     onTrash : function(e)
36232     {
36233         e.preventDefault();
36234         
36235         this.fireEvent('trash', this);
36236     }
36237     
36238 });
36239 /*
36240  * - LGPL
36241  *
36242  * FieldLabel
36243  * 
36244  */
36245
36246 /**
36247  * @class Roo.bootstrap.form.FieldLabel
36248  * @extends Roo.bootstrap.Component
36249  * Bootstrap FieldLabel class
36250  * @cfg {String} html contents of the element
36251  * @cfg {String} tag tag of the element default label
36252  * @cfg {String} cls class of the element
36253  * @cfg {String} target label target 
36254  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36255  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36256  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36257  * @cfg {String} iconTooltip default "This field is required"
36258  * @cfg {String} indicatorpos (left|right) default left
36259  * 
36260  * @constructor
36261  * Create a new FieldLabel
36262  * @param {Object} config The config object
36263  */
36264
36265 Roo.bootstrap.form.FieldLabel = function(config){
36266     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36267     
36268     this.addEvents({
36269             /**
36270              * @event invalid
36271              * Fires after the field has been marked as invalid.
36272              * @param {Roo.form.FieldLabel} this
36273              * @param {String} msg The validation message
36274              */
36275             invalid : true,
36276             /**
36277              * @event valid
36278              * Fires after the field has been validated with no errors.
36279              * @param {Roo.form.FieldLabel} this
36280              */
36281             valid : true
36282         });
36283 };
36284
36285 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36286     
36287     tag: 'label',
36288     cls: '',
36289     html: '',
36290     target: '',
36291     allowBlank : true,
36292     invalidClass : 'has-warning',
36293     validClass : 'has-success',
36294     iconTooltip : 'This field is required',
36295     indicatorpos : 'left',
36296     
36297     getAutoCreate : function(){
36298         
36299         var cls = "";
36300         if (!this.allowBlank) {
36301             cls  = "visible";
36302         }
36303         
36304         var cfg = {
36305             tag : this.tag,
36306             cls : 'roo-bootstrap-field-label ' + this.cls,
36307             for : this.target,
36308             cn : [
36309                 {
36310                     tag : 'i',
36311                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36312                     tooltip : this.iconTooltip
36313                 },
36314                 {
36315                     tag : 'span',
36316                     html : this.html
36317                 }
36318             ] 
36319         };
36320         
36321         if(this.indicatorpos == 'right'){
36322             var cfg = {
36323                 tag : this.tag,
36324                 cls : 'roo-bootstrap-field-label ' + this.cls,
36325                 for : this.target,
36326                 cn : [
36327                     {
36328                         tag : 'span',
36329                         html : this.html
36330                     },
36331                     {
36332                         tag : 'i',
36333                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36334                         tooltip : this.iconTooltip
36335                     }
36336                 ] 
36337             };
36338         }
36339         
36340         return cfg;
36341     },
36342     
36343     initEvents: function() 
36344     {
36345         Roo.bootstrap.Element.superclass.initEvents.call(this);
36346         
36347         this.indicator = this.indicatorEl();
36348         
36349         if(this.indicator){
36350             this.indicator.removeClass('visible');
36351             this.indicator.addClass('invisible');
36352         }
36353         
36354         Roo.bootstrap.form.FieldLabel.register(this);
36355     },
36356     
36357     indicatorEl : function()
36358     {
36359         var indicator = this.el.select('i.roo-required-indicator',true).first();
36360         
36361         if(!indicator){
36362             return false;
36363         }
36364         
36365         return indicator;
36366         
36367     },
36368     
36369     /**
36370      * Mark this field as valid
36371      */
36372     markValid : function()
36373     {
36374         if(this.indicator){
36375             this.indicator.removeClass('visible');
36376             this.indicator.addClass('invisible');
36377         }
36378         if (Roo.bootstrap.version == 3) {
36379             this.el.removeClass(this.invalidClass);
36380             this.el.addClass(this.validClass);
36381         } else {
36382             this.el.removeClass('is-invalid');
36383             this.el.addClass('is-valid');
36384         }
36385         
36386         
36387         this.fireEvent('valid', this);
36388     },
36389     
36390     /**
36391      * Mark this field as invalid
36392      * @param {String} msg The validation message
36393      */
36394     markInvalid : function(msg)
36395     {
36396         if(this.indicator){
36397             this.indicator.removeClass('invisible');
36398             this.indicator.addClass('visible');
36399         }
36400           if (Roo.bootstrap.version == 3) {
36401             this.el.removeClass(this.validClass);
36402             this.el.addClass(this.invalidClass);
36403         } else {
36404             this.el.removeClass('is-valid');
36405             this.el.addClass('is-invalid');
36406         }
36407         
36408         
36409         this.fireEvent('invalid', this, msg);
36410     }
36411     
36412    
36413 });
36414
36415 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36416     
36417     groups: {},
36418     
36419      /**
36420     * register a FieldLabel Group
36421     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36422     */
36423     register : function(label)
36424     {
36425         if(this.groups.hasOwnProperty(label.target)){
36426             return;
36427         }
36428      
36429         this.groups[label.target] = label;
36430         
36431     },
36432     /**
36433     * fetch a FieldLabel Group based on the target
36434     * @param {string} target
36435     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36436     */
36437     get: function(target) {
36438         if (typeof(this.groups[target]) == 'undefined') {
36439             return false;
36440         }
36441         
36442         return this.groups[target] ;
36443     }
36444 });
36445
36446  
36447
36448  /*
36449  * - LGPL
36450  *
36451  * page DateSplitField.
36452  * 
36453  */
36454
36455
36456 /**
36457  * @class Roo.bootstrap.form.DateSplitField
36458  * @extends Roo.bootstrap.Component
36459  * Bootstrap DateSplitField class
36460  * @cfg {string} fieldLabel - the label associated
36461  * @cfg {Number} labelWidth set the width of label (0-12)
36462  * @cfg {String} labelAlign (top|left)
36463  * @cfg {Boolean} dayAllowBlank (true|false) default false
36464  * @cfg {Boolean} monthAllowBlank (true|false) default false
36465  * @cfg {Boolean} yearAllowBlank (true|false) default false
36466  * @cfg {string} dayPlaceholder 
36467  * @cfg {string} monthPlaceholder
36468  * @cfg {string} yearPlaceholder
36469  * @cfg {string} dayFormat default 'd'
36470  * @cfg {string} monthFormat default 'm'
36471  * @cfg {string} yearFormat default 'Y'
36472  * @cfg {Number} labellg set the width of label (1-12)
36473  * @cfg {Number} labelmd set the width of label (1-12)
36474  * @cfg {Number} labelsm set the width of label (1-12)
36475  * @cfg {Number} labelxs set the width of label (1-12)
36476
36477  *     
36478  * @constructor
36479  * Create a new DateSplitField
36480  * @param {Object} config The config object
36481  */
36482
36483 Roo.bootstrap.form.DateSplitField = function(config){
36484     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36485     
36486     this.addEvents({
36487         // raw events
36488          /**
36489          * @event years
36490          * getting the data of years
36491          * @param {Roo.bootstrap.form.DateSplitField} this
36492          * @param {Object} years
36493          */
36494         "years" : true,
36495         /**
36496          * @event days
36497          * getting the data of days
36498          * @param {Roo.bootstrap.form.DateSplitField} this
36499          * @param {Object} days
36500          */
36501         "days" : true,
36502         /**
36503          * @event invalid
36504          * Fires after the field has been marked as invalid.
36505          * @param {Roo.form.Field} this
36506          * @param {String} msg The validation message
36507          */
36508         invalid : true,
36509        /**
36510          * @event valid
36511          * Fires after the field has been validated with no errors.
36512          * @param {Roo.form.Field} this
36513          */
36514         valid : true
36515     });
36516 };
36517
36518 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36519     
36520     fieldLabel : '',
36521     labelAlign : 'top',
36522     labelWidth : 3,
36523     dayAllowBlank : false,
36524     monthAllowBlank : false,
36525     yearAllowBlank : false,
36526     dayPlaceholder : '',
36527     monthPlaceholder : '',
36528     yearPlaceholder : '',
36529     dayFormat : 'd',
36530     monthFormat : 'm',
36531     yearFormat : 'Y',
36532     isFormField : true,
36533     labellg : 0,
36534     labelmd : 0,
36535     labelsm : 0,
36536     labelxs : 0,
36537     
36538     getAutoCreate : function()
36539     {
36540         var cfg = {
36541             tag : 'div',
36542             cls : 'row roo-date-split-field-group',
36543             cn : [
36544                 {
36545                     tag : 'input',
36546                     type : 'hidden',
36547                     cls : 'form-hidden-field roo-date-split-field-group-value',
36548                     name : this.name
36549                 }
36550             ]
36551         };
36552         
36553         var labelCls = 'col-md-12';
36554         var contentCls = 'col-md-4';
36555         
36556         if(this.fieldLabel){
36557             
36558             var label = {
36559                 tag : 'div',
36560                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36561                 cn : [
36562                     {
36563                         tag : 'label',
36564                         html : this.fieldLabel
36565                     }
36566                 ]
36567             };
36568             
36569             if(this.labelAlign == 'left'){
36570             
36571                 if(this.labelWidth > 12){
36572                     label.style = "width: " + this.labelWidth + 'px';
36573                 }
36574
36575                 if(this.labelWidth < 13 && this.labelmd == 0){
36576                     this.labelmd = this.labelWidth;
36577                 }
36578
36579                 if(this.labellg > 0){
36580                     labelCls = ' col-lg-' + this.labellg;
36581                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36582                 }
36583
36584                 if(this.labelmd > 0){
36585                     labelCls = ' col-md-' + this.labelmd;
36586                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36587                 }
36588
36589                 if(this.labelsm > 0){
36590                     labelCls = ' col-sm-' + this.labelsm;
36591                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36592                 }
36593
36594                 if(this.labelxs > 0){
36595                     labelCls = ' col-xs-' + this.labelxs;
36596                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36597                 }
36598             }
36599             
36600             label.cls += ' ' + labelCls;
36601             
36602             cfg.cn.push(label);
36603         }
36604         
36605         Roo.each(['day', 'month', 'year'], function(t){
36606             cfg.cn.push({
36607                 tag : 'div',
36608                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36609             });
36610         }, this);
36611         
36612         return cfg;
36613     },
36614     
36615     inputEl: function ()
36616     {
36617         return this.el.select('.roo-date-split-field-group-value', true).first();
36618     },
36619     
36620     onRender : function(ct, position) 
36621     {
36622         var _this = this;
36623         
36624         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36625         
36626         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36627         
36628         this.dayField = new Roo.bootstrap.form.ComboBox({
36629             allowBlank : this.dayAllowBlank,
36630             alwaysQuery : true,
36631             displayField : 'value',
36632             editable : false,
36633             fieldLabel : '',
36634             forceSelection : true,
36635             mode : 'local',
36636             placeholder : this.dayPlaceholder,
36637             selectOnFocus : true,
36638             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36639             triggerAction : 'all',
36640             typeAhead : true,
36641             valueField : 'value',
36642             store : new Roo.data.SimpleStore({
36643                 data : (function() {    
36644                     var days = [];
36645                     _this.fireEvent('days', _this, days);
36646                     return days;
36647                 })(),
36648                 fields : [ 'value' ]
36649             }),
36650             listeners : {
36651                 select : function (_self, record, index)
36652                 {
36653                     _this.setValue(_this.getValue());
36654                 }
36655             }
36656         });
36657
36658         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36659         
36660         this.monthField = new Roo.bootstrap.form.MonthField({
36661             after : '<i class=\"fa fa-calendar\"></i>',
36662             allowBlank : this.monthAllowBlank,
36663             placeholder : this.monthPlaceholder,
36664             readOnly : true,
36665             listeners : {
36666                 render : function (_self)
36667                 {
36668                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36669                         e.preventDefault();
36670                         _self.focus();
36671                     });
36672                 },
36673                 select : function (_self, oldvalue, newvalue)
36674                 {
36675                     _this.setValue(_this.getValue());
36676                 }
36677             }
36678         });
36679         
36680         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36681         
36682         this.yearField = new Roo.bootstrap.form.ComboBox({
36683             allowBlank : this.yearAllowBlank,
36684             alwaysQuery : true,
36685             displayField : 'value',
36686             editable : false,
36687             fieldLabel : '',
36688             forceSelection : true,
36689             mode : 'local',
36690             placeholder : this.yearPlaceholder,
36691             selectOnFocus : true,
36692             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36693             triggerAction : 'all',
36694             typeAhead : true,
36695             valueField : 'value',
36696             store : new Roo.data.SimpleStore({
36697                 data : (function() {
36698                     var years = [];
36699                     _this.fireEvent('years', _this, years);
36700                     return years;
36701                 })(),
36702                 fields : [ 'value' ]
36703             }),
36704             listeners : {
36705                 select : function (_self, record, index)
36706                 {
36707                     _this.setValue(_this.getValue());
36708                 }
36709             }
36710         });
36711
36712         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36713     },
36714     
36715     setValue : function(v, format)
36716     {
36717         this.inputEl.dom.value = v;
36718         
36719         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36720         
36721         var d = Date.parseDate(v, f);
36722         
36723         if(!d){
36724             this.validate();
36725             return;
36726         }
36727         
36728         this.setDay(d.format(this.dayFormat));
36729         this.setMonth(d.format(this.monthFormat));
36730         this.setYear(d.format(this.yearFormat));
36731         
36732         this.validate();
36733         
36734         return;
36735     },
36736     
36737     setDay : function(v)
36738     {
36739         this.dayField.setValue(v);
36740         this.inputEl.dom.value = this.getValue();
36741         this.validate();
36742         return;
36743     },
36744     
36745     setMonth : function(v)
36746     {
36747         this.monthField.setValue(v, true);
36748         this.inputEl.dom.value = this.getValue();
36749         this.validate();
36750         return;
36751     },
36752     
36753     setYear : function(v)
36754     {
36755         this.yearField.setValue(v);
36756         this.inputEl.dom.value = this.getValue();
36757         this.validate();
36758         return;
36759     },
36760     
36761     getDay : function()
36762     {
36763         return this.dayField.getValue();
36764     },
36765     
36766     getMonth : function()
36767     {
36768         return this.monthField.getValue();
36769     },
36770     
36771     getYear : function()
36772     {
36773         return this.yearField.getValue();
36774     },
36775     
36776     getValue : function()
36777     {
36778         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36779         
36780         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36781         
36782         return date;
36783     },
36784     
36785     reset : function()
36786     {
36787         this.setDay('');
36788         this.setMonth('');
36789         this.setYear('');
36790         this.inputEl.dom.value = '';
36791         this.validate();
36792         return;
36793     },
36794     
36795     validate : function()
36796     {
36797         var d = this.dayField.validate();
36798         var m = this.monthField.validate();
36799         var y = this.yearField.validate();
36800         
36801         var valid = true;
36802         
36803         if(
36804                 (!this.dayAllowBlank && !d) ||
36805                 (!this.monthAllowBlank && !m) ||
36806                 (!this.yearAllowBlank && !y)
36807         ){
36808             valid = false;
36809         }
36810         
36811         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36812             return valid;
36813         }
36814         
36815         if(valid){
36816             this.markValid();
36817             return valid;
36818         }
36819         
36820         this.markInvalid();
36821         
36822         return valid;
36823     },
36824     
36825     markValid : function()
36826     {
36827         
36828         var label = this.el.select('label', true).first();
36829         var icon = this.el.select('i.fa-star', true).first();
36830
36831         if(label && icon){
36832             icon.remove();
36833         }
36834         
36835         this.fireEvent('valid', this);
36836     },
36837     
36838      /**
36839      * Mark this field as invalid
36840      * @param {String} msg The validation message
36841      */
36842     markInvalid : function(msg)
36843     {
36844         
36845         var label = this.el.select('label', true).first();
36846         var icon = this.el.select('i.fa-star', true).first();
36847
36848         if(label && !icon){
36849             this.el.select('.roo-date-split-field-label', true).createChild({
36850                 tag : 'i',
36851                 cls : 'text-danger fa fa-lg fa-star',
36852                 tooltip : 'This field is required',
36853                 style : 'margin-right:5px;'
36854             }, label, true);
36855         }
36856         
36857         this.fireEvent('invalid', this, msg);
36858     },
36859     
36860     clearInvalid : function()
36861     {
36862         var label = this.el.select('label', true).first();
36863         var icon = this.el.select('i.fa-star', true).first();
36864
36865         if(label && icon){
36866             icon.remove();
36867         }
36868         
36869         this.fireEvent('valid', this);
36870     },
36871     
36872     getName: function()
36873     {
36874         return this.name;
36875     }
36876     
36877 });
36878
36879  
36880
36881 /**
36882  * @class Roo.bootstrap.LayoutMasonry
36883  * @extends Roo.bootstrap.Component
36884  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36885  * Bootstrap Layout Masonry class
36886  *
36887  * This is based on 
36888  * http://masonry.desandro.com
36889  *
36890  * The idea is to render all the bricks based on vertical width...
36891  *
36892  * The original code extends 'outlayer' - we might need to use that....
36893
36894  * @constructor
36895  * Create a new Element
36896  * @param {Object} config The config object
36897  */
36898
36899 Roo.bootstrap.LayoutMasonry = function(config){
36900     
36901     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36902     
36903     this.bricks = [];
36904     
36905     Roo.bootstrap.LayoutMasonry.register(this);
36906     
36907     this.addEvents({
36908         // raw events
36909         /**
36910          * @event layout
36911          * Fire after layout the items
36912          * @param {Roo.bootstrap.LayoutMasonry} this
36913          * @param {Roo.EventObject} e
36914          */
36915         "layout" : true
36916     });
36917     
36918 };
36919
36920 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36921     
36922     /**
36923      * @cfg {Boolean} isLayoutInstant = no animation?
36924      */   
36925     isLayoutInstant : false, // needed?
36926    
36927     /**
36928      * @cfg {Number} boxWidth  width of the columns
36929      */   
36930     boxWidth : 450,
36931     
36932       /**
36933      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36934      */   
36935     boxHeight : 0,
36936     
36937     /**
36938      * @cfg {Number} padWidth padding below box..
36939      */   
36940     padWidth : 10, 
36941     
36942     /**
36943      * @cfg {Number} gutter gutter width..
36944      */   
36945     gutter : 10,
36946     
36947      /**
36948      * @cfg {Number} maxCols maximum number of columns
36949      */   
36950     
36951     maxCols: 0,
36952     
36953     /**
36954      * @cfg {Boolean} isAutoInitial defalut true
36955      */   
36956     isAutoInitial : true, 
36957     
36958     containerWidth: 0,
36959     
36960     /**
36961      * @cfg {Boolean} isHorizontal defalut false
36962      */   
36963     isHorizontal : false, 
36964
36965     currentSize : null,
36966     
36967     tag: 'div',
36968     
36969     cls: '',
36970     
36971     bricks: null, //CompositeElement
36972     
36973     cols : 1,
36974     
36975     _isLayoutInited : false,
36976     
36977 //    isAlternative : false, // only use for vertical layout...
36978     
36979     /**
36980      * @cfg {Number} alternativePadWidth padding below box..
36981      */   
36982     alternativePadWidth : 50,
36983     
36984     selectedBrick : [],
36985     
36986     getAutoCreate : function(){
36987         
36988         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36989         
36990         var cfg = {
36991             tag: this.tag,
36992             cls: 'blog-masonary-wrapper ' + this.cls,
36993             cn : {
36994                 cls : 'mas-boxes masonary'
36995             }
36996         };
36997         
36998         return cfg;
36999     },
37000     
37001     getChildContainer: function( )
37002     {
37003         if (this.boxesEl) {
37004             return this.boxesEl;
37005         }
37006         
37007         this.boxesEl = this.el.select('.mas-boxes').first();
37008         
37009         return this.boxesEl;
37010     },
37011     
37012     
37013     initEvents : function()
37014     {
37015         var _this = this;
37016         
37017         if(this.isAutoInitial){
37018             Roo.log('hook children rendered');
37019             this.on('childrenrendered', function() {
37020                 Roo.log('children rendered');
37021                 _this.initial();
37022             } ,this);
37023         }
37024     },
37025     
37026     initial : function()
37027     {
37028         this.selectedBrick = [];
37029         
37030         this.currentSize = this.el.getBox(true);
37031         
37032         Roo.EventManager.onWindowResize(this.resize, this); 
37033
37034         if(!this.isAutoInitial){
37035             this.layout();
37036             return;
37037         }
37038         
37039         this.layout();
37040         
37041         return;
37042         //this.layout.defer(500,this);
37043         
37044     },
37045     
37046     resize : function()
37047     {
37048         var cs = this.el.getBox(true);
37049         
37050         if (
37051                 this.currentSize.width == cs.width && 
37052                 this.currentSize.x == cs.x && 
37053                 this.currentSize.height == cs.height && 
37054                 this.currentSize.y == cs.y 
37055         ) {
37056             Roo.log("no change in with or X or Y");
37057             return;
37058         }
37059         
37060         this.currentSize = cs;
37061         
37062         this.layout();
37063         
37064     },
37065     
37066     layout : function()
37067     {   
37068         this._resetLayout();
37069         
37070         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37071         
37072         this.layoutItems( isInstant );
37073       
37074         this._isLayoutInited = true;
37075         
37076         this.fireEvent('layout', this);
37077         
37078     },
37079     
37080     _resetLayout : function()
37081     {
37082         if(this.isHorizontal){
37083             this.horizontalMeasureColumns();
37084             return;
37085         }
37086         
37087         this.verticalMeasureColumns();
37088         
37089     },
37090     
37091     verticalMeasureColumns : function()
37092     {
37093         this.getContainerWidth();
37094         
37095 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37096 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37097 //            return;
37098 //        }
37099         
37100         var boxWidth = this.boxWidth + this.padWidth;
37101         
37102         if(this.containerWidth < this.boxWidth){
37103             boxWidth = this.containerWidth
37104         }
37105         
37106         var containerWidth = this.containerWidth;
37107         
37108         var cols = Math.floor(containerWidth / boxWidth);
37109         
37110         this.cols = Math.max( cols, 1 );
37111         
37112         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37113         
37114         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37115         
37116         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37117         
37118         this.colWidth = boxWidth + avail - this.padWidth;
37119         
37120         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37121         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37122     },
37123     
37124     horizontalMeasureColumns : function()
37125     {
37126         this.getContainerWidth();
37127         
37128         var boxWidth = this.boxWidth;
37129         
37130         if(this.containerWidth < boxWidth){
37131             boxWidth = this.containerWidth;
37132         }
37133         
37134         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37135         
37136         this.el.setHeight(boxWidth);
37137         
37138     },
37139     
37140     getContainerWidth : function()
37141     {
37142         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37143     },
37144     
37145     layoutItems : function( isInstant )
37146     {
37147         Roo.log(this.bricks);
37148         
37149         var items = Roo.apply([], this.bricks);
37150         
37151         if(this.isHorizontal){
37152             this._horizontalLayoutItems( items , isInstant );
37153             return;
37154         }
37155         
37156 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37157 //            this._verticalAlternativeLayoutItems( items , isInstant );
37158 //            return;
37159 //        }
37160         
37161         this._verticalLayoutItems( items , isInstant );
37162         
37163     },
37164     
37165     _verticalLayoutItems : function ( items , isInstant)
37166     {
37167         if ( !items || !items.length ) {
37168             return;
37169         }
37170         
37171         var standard = [
37172             ['xs', 'xs', 'xs', 'tall'],
37173             ['xs', 'xs', 'tall'],
37174             ['xs', 'xs', 'sm'],
37175             ['xs', 'xs', 'xs'],
37176             ['xs', 'tall'],
37177             ['xs', 'sm'],
37178             ['xs', 'xs'],
37179             ['xs'],
37180             
37181             ['sm', 'xs', 'xs'],
37182             ['sm', 'xs'],
37183             ['sm'],
37184             
37185             ['tall', 'xs', 'xs', 'xs'],
37186             ['tall', 'xs', 'xs'],
37187             ['tall', 'xs'],
37188             ['tall']
37189             
37190         ];
37191         
37192         var queue = [];
37193         
37194         var boxes = [];
37195         
37196         var box = [];
37197         
37198         Roo.each(items, function(item, k){
37199             
37200             switch (item.size) {
37201                 // these layouts take up a full box,
37202                 case 'md' :
37203                 case 'md-left' :
37204                 case 'md-right' :
37205                 case 'wide' :
37206                     
37207                     if(box.length){
37208                         boxes.push(box);
37209                         box = [];
37210                     }
37211                     
37212                     boxes.push([item]);
37213                     
37214                     break;
37215                     
37216                 case 'xs' :
37217                 case 'sm' :
37218                 case 'tall' :
37219                     
37220                     box.push(item);
37221                     
37222                     break;
37223                 default :
37224                     break;
37225                     
37226             }
37227             
37228         }, this);
37229         
37230         if(box.length){
37231             boxes.push(box);
37232             box = [];
37233         }
37234         
37235         var filterPattern = function(box, length)
37236         {
37237             if(!box.length){
37238                 return;
37239             }
37240             
37241             var match = false;
37242             
37243             var pattern = box.slice(0, length);
37244             
37245             var format = [];
37246             
37247             Roo.each(pattern, function(i){
37248                 format.push(i.size);
37249             }, this);
37250             
37251             Roo.each(standard, function(s){
37252                 
37253                 if(String(s) != String(format)){
37254                     return;
37255                 }
37256                 
37257                 match = true;
37258                 return false;
37259                 
37260             }, this);
37261             
37262             if(!match && length == 1){
37263                 return;
37264             }
37265             
37266             if(!match){
37267                 filterPattern(box, length - 1);
37268                 return;
37269             }
37270                 
37271             queue.push(pattern);
37272
37273             box = box.slice(length, box.length);
37274
37275             filterPattern(box, 4);
37276
37277             return;
37278             
37279         }
37280         
37281         Roo.each(boxes, function(box, k){
37282             
37283             if(!box.length){
37284                 return;
37285             }
37286             
37287             if(box.length == 1){
37288                 queue.push(box);
37289                 return;
37290             }
37291             
37292             filterPattern(box, 4);
37293             
37294         }, this);
37295         
37296         this._processVerticalLayoutQueue( queue, isInstant );
37297         
37298     },
37299     
37300 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37301 //    {
37302 //        if ( !items || !items.length ) {
37303 //            return;
37304 //        }
37305 //
37306 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37307 //        
37308 //    },
37309     
37310     _horizontalLayoutItems : function ( items , isInstant)
37311     {
37312         if ( !items || !items.length || items.length < 3) {
37313             return;
37314         }
37315         
37316         items.reverse();
37317         
37318         var eItems = items.slice(0, 3);
37319         
37320         items = items.slice(3, items.length);
37321         
37322         var standard = [
37323             ['xs', 'xs', 'xs', 'wide'],
37324             ['xs', 'xs', 'wide'],
37325             ['xs', 'xs', 'sm'],
37326             ['xs', 'xs', 'xs'],
37327             ['xs', 'wide'],
37328             ['xs', 'sm'],
37329             ['xs', 'xs'],
37330             ['xs'],
37331             
37332             ['sm', 'xs', 'xs'],
37333             ['sm', 'xs'],
37334             ['sm'],
37335             
37336             ['wide', 'xs', 'xs', 'xs'],
37337             ['wide', 'xs', 'xs'],
37338             ['wide', 'xs'],
37339             ['wide'],
37340             
37341             ['wide-thin']
37342         ];
37343         
37344         var queue = [];
37345         
37346         var boxes = [];
37347         
37348         var box = [];
37349         
37350         Roo.each(items, function(item, k){
37351             
37352             switch (item.size) {
37353                 case 'md' :
37354                 case 'md-left' :
37355                 case 'md-right' :
37356                 case 'tall' :
37357                     
37358                     if(box.length){
37359                         boxes.push(box);
37360                         box = [];
37361                     }
37362                     
37363                     boxes.push([item]);
37364                     
37365                     break;
37366                     
37367                 case 'xs' :
37368                 case 'sm' :
37369                 case 'wide' :
37370                 case 'wide-thin' :
37371                     
37372                     box.push(item);
37373                     
37374                     break;
37375                 default :
37376                     break;
37377                     
37378             }
37379             
37380         }, this);
37381         
37382         if(box.length){
37383             boxes.push(box);
37384             box = [];
37385         }
37386         
37387         var filterPattern = function(box, length)
37388         {
37389             if(!box.length){
37390                 return;
37391             }
37392             
37393             var match = false;
37394             
37395             var pattern = box.slice(0, length);
37396             
37397             var format = [];
37398             
37399             Roo.each(pattern, function(i){
37400                 format.push(i.size);
37401             }, this);
37402             
37403             Roo.each(standard, function(s){
37404                 
37405                 if(String(s) != String(format)){
37406                     return;
37407                 }
37408                 
37409                 match = true;
37410                 return false;
37411                 
37412             }, this);
37413             
37414             if(!match && length == 1){
37415                 return;
37416             }
37417             
37418             if(!match){
37419                 filterPattern(box, length - 1);
37420                 return;
37421             }
37422                 
37423             queue.push(pattern);
37424
37425             box = box.slice(length, box.length);
37426
37427             filterPattern(box, 4);
37428
37429             return;
37430             
37431         }
37432         
37433         Roo.each(boxes, function(box, k){
37434             
37435             if(!box.length){
37436                 return;
37437             }
37438             
37439             if(box.length == 1){
37440                 queue.push(box);
37441                 return;
37442             }
37443             
37444             filterPattern(box, 4);
37445             
37446         }, this);
37447         
37448         
37449         var prune = [];
37450         
37451         var pos = this.el.getBox(true);
37452         
37453         var minX = pos.x;
37454         
37455         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37456         
37457         var hit_end = false;
37458         
37459         Roo.each(queue, function(box){
37460             
37461             if(hit_end){
37462                 
37463                 Roo.each(box, function(b){
37464                 
37465                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37466                     b.el.hide();
37467
37468                 }, this);
37469
37470                 return;
37471             }
37472             
37473             var mx = 0;
37474             
37475             Roo.each(box, function(b){
37476                 
37477                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37478                 b.el.show();
37479
37480                 mx = Math.max(mx, b.x);
37481                 
37482             }, this);
37483             
37484             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37485             
37486             if(maxX < minX){
37487                 
37488                 Roo.each(box, function(b){
37489                 
37490                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37491                     b.el.hide();
37492                     
37493                 }, this);
37494                 
37495                 hit_end = true;
37496                 
37497                 return;
37498             }
37499             
37500             prune.push(box);
37501             
37502         }, this);
37503         
37504         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37505     },
37506     
37507     /** Sets position of item in DOM
37508     * @param {Element} item
37509     * @param {Number} x - horizontal position
37510     * @param {Number} y - vertical position
37511     * @param {Boolean} isInstant - disables transitions
37512     */
37513     _processVerticalLayoutQueue : function( queue, isInstant )
37514     {
37515         var pos = this.el.getBox(true);
37516         var x = pos.x;
37517         var y = pos.y;
37518         var maxY = [];
37519         
37520         for (var i = 0; i < this.cols; i++){
37521             maxY[i] = pos.y;
37522         }
37523         
37524         Roo.each(queue, function(box, k){
37525             
37526             var col = k % this.cols;
37527             
37528             Roo.each(box, function(b,kk){
37529                 
37530                 b.el.position('absolute');
37531                 
37532                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37533                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37534                 
37535                 if(b.size == 'md-left' || b.size == 'md-right'){
37536                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37537                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37538                 }
37539                 
37540                 b.el.setWidth(width);
37541                 b.el.setHeight(height);
37542                 // iframe?
37543                 b.el.select('iframe',true).setSize(width,height);
37544                 
37545             }, this);
37546             
37547             for (var i = 0; i < this.cols; i++){
37548                 
37549                 if(maxY[i] < maxY[col]){
37550                     col = i;
37551                     continue;
37552                 }
37553                 
37554                 col = Math.min(col, i);
37555                 
37556             }
37557             
37558             x = pos.x + col * (this.colWidth + this.padWidth);
37559             
37560             y = maxY[col];
37561             
37562             var positions = [];
37563             
37564             switch (box.length){
37565                 case 1 :
37566                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37567                     break;
37568                 case 2 :
37569                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37570                     break;
37571                 case 3 :
37572                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37573                     break;
37574                 case 4 :
37575                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37576                     break;
37577                 default :
37578                     break;
37579             }
37580             
37581             Roo.each(box, function(b,kk){
37582                 
37583                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37584                 
37585                 var sz = b.el.getSize();
37586                 
37587                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37588                 
37589             }, this);
37590             
37591         }, this);
37592         
37593         var mY = 0;
37594         
37595         for (var i = 0; i < this.cols; i++){
37596             mY = Math.max(mY, maxY[i]);
37597         }
37598         
37599         this.el.setHeight(mY - pos.y);
37600         
37601     },
37602     
37603 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37604 //    {
37605 //        var pos = this.el.getBox(true);
37606 //        var x = pos.x;
37607 //        var y = pos.y;
37608 //        var maxX = pos.right;
37609 //        
37610 //        var maxHeight = 0;
37611 //        
37612 //        Roo.each(items, function(item, k){
37613 //            
37614 //            var c = k % 2;
37615 //            
37616 //            item.el.position('absolute');
37617 //                
37618 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37619 //
37620 //            item.el.setWidth(width);
37621 //
37622 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37623 //
37624 //            item.el.setHeight(height);
37625 //            
37626 //            if(c == 0){
37627 //                item.el.setXY([x, y], isInstant ? false : true);
37628 //            } else {
37629 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37630 //            }
37631 //            
37632 //            y = y + height + this.alternativePadWidth;
37633 //            
37634 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37635 //            
37636 //        }, this);
37637 //        
37638 //        this.el.setHeight(maxHeight);
37639 //        
37640 //    },
37641     
37642     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37643     {
37644         var pos = this.el.getBox(true);
37645         
37646         var minX = pos.x;
37647         var minY = pos.y;
37648         
37649         var maxX = pos.right;
37650         
37651         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37652         
37653         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37654         
37655         Roo.each(queue, function(box, k){
37656             
37657             Roo.each(box, function(b, kk){
37658                 
37659                 b.el.position('absolute');
37660                 
37661                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37662                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37663                 
37664                 if(b.size == 'md-left' || b.size == 'md-right'){
37665                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37666                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37667                 }
37668                 
37669                 b.el.setWidth(width);
37670                 b.el.setHeight(height);
37671                 
37672             }, this);
37673             
37674             if(!box.length){
37675                 return;
37676             }
37677             
37678             var positions = [];
37679             
37680             switch (box.length){
37681                 case 1 :
37682                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37683                     break;
37684                 case 2 :
37685                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37686                     break;
37687                 case 3 :
37688                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37689                     break;
37690                 case 4 :
37691                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37692                     break;
37693                 default :
37694                     break;
37695             }
37696             
37697             Roo.each(box, function(b,kk){
37698                 
37699                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37700                 
37701                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37702                 
37703             }, this);
37704             
37705         }, this);
37706         
37707     },
37708     
37709     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37710     {
37711         Roo.each(eItems, function(b,k){
37712             
37713             b.size = (k == 0) ? 'sm' : 'xs';
37714             b.x = (k == 0) ? 2 : 1;
37715             b.y = (k == 0) ? 2 : 1;
37716             
37717             b.el.position('absolute');
37718             
37719             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37720                 
37721             b.el.setWidth(width);
37722             
37723             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37724             
37725             b.el.setHeight(height);
37726             
37727         }, this);
37728
37729         var positions = [];
37730         
37731         positions.push({
37732             x : maxX - this.unitWidth * 2 - this.gutter,
37733             y : minY
37734         });
37735         
37736         positions.push({
37737             x : maxX - this.unitWidth,
37738             y : minY + (this.unitWidth + this.gutter) * 2
37739         });
37740         
37741         positions.push({
37742             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37743             y : minY
37744         });
37745         
37746         Roo.each(eItems, function(b,k){
37747             
37748             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37749
37750         }, this);
37751         
37752     },
37753     
37754     getVerticalOneBoxColPositions : function(x, y, box)
37755     {
37756         var pos = [];
37757         
37758         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37759         
37760         if(box[0].size == 'md-left'){
37761             rand = 0;
37762         }
37763         
37764         if(box[0].size == 'md-right'){
37765             rand = 1;
37766         }
37767         
37768         pos.push({
37769             x : x + (this.unitWidth + this.gutter) * rand,
37770             y : y
37771         });
37772         
37773         return pos;
37774     },
37775     
37776     getVerticalTwoBoxColPositions : function(x, y, box)
37777     {
37778         var pos = [];
37779         
37780         if(box[0].size == 'xs'){
37781             
37782             pos.push({
37783                 x : x,
37784                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37785             });
37786
37787             pos.push({
37788                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37789                 y : y
37790             });
37791             
37792             return pos;
37793             
37794         }
37795         
37796         pos.push({
37797             x : x,
37798             y : y
37799         });
37800
37801         pos.push({
37802             x : x + (this.unitWidth + this.gutter) * 2,
37803             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37804         });
37805         
37806         return pos;
37807         
37808     },
37809     
37810     getVerticalThreeBoxColPositions : function(x, y, box)
37811     {
37812         var pos = [];
37813         
37814         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37815             
37816             pos.push({
37817                 x : x,
37818                 y : y
37819             });
37820
37821             pos.push({
37822                 x : x + (this.unitWidth + this.gutter) * 1,
37823                 y : y
37824             });
37825             
37826             pos.push({
37827                 x : x + (this.unitWidth + this.gutter) * 2,
37828                 y : y
37829             });
37830             
37831             return pos;
37832             
37833         }
37834         
37835         if(box[0].size == 'xs' && box[1].size == 'xs'){
37836             
37837             pos.push({
37838                 x : x,
37839                 y : y
37840             });
37841
37842             pos.push({
37843                 x : x,
37844                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37845             });
37846             
37847             pos.push({
37848                 x : x + (this.unitWidth + this.gutter) * 1,
37849                 y : y
37850             });
37851             
37852             return pos;
37853             
37854         }
37855         
37856         pos.push({
37857             x : x,
37858             y : y
37859         });
37860
37861         pos.push({
37862             x : x + (this.unitWidth + this.gutter) * 2,
37863             y : y
37864         });
37865
37866         pos.push({
37867             x : x + (this.unitWidth + this.gutter) * 2,
37868             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37869         });
37870             
37871         return pos;
37872         
37873     },
37874     
37875     getVerticalFourBoxColPositions : function(x, y, box)
37876     {
37877         var pos = [];
37878         
37879         if(box[0].size == 'xs'){
37880             
37881             pos.push({
37882                 x : x,
37883                 y : y
37884             });
37885
37886             pos.push({
37887                 x : x,
37888                 y : y + (this.unitHeight + this.gutter) * 1
37889             });
37890             
37891             pos.push({
37892                 x : x,
37893                 y : y + (this.unitHeight + this.gutter) * 2
37894             });
37895             
37896             pos.push({
37897                 x : x + (this.unitWidth + this.gutter) * 1,
37898                 y : y
37899             });
37900             
37901             return pos;
37902             
37903         }
37904         
37905         pos.push({
37906             x : x,
37907             y : y
37908         });
37909
37910         pos.push({
37911             x : x + (this.unitWidth + this.gutter) * 2,
37912             y : y
37913         });
37914
37915         pos.push({
37916             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37917             y : y + (this.unitHeight + this.gutter) * 1
37918         });
37919
37920         pos.push({
37921             x : x + (this.unitWidth + this.gutter) * 2,
37922             y : y + (this.unitWidth + this.gutter) * 2
37923         });
37924
37925         return pos;
37926         
37927     },
37928     
37929     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37930     {
37931         var pos = [];
37932         
37933         if(box[0].size == 'md-left'){
37934             pos.push({
37935                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37936                 y : minY
37937             });
37938             
37939             return pos;
37940         }
37941         
37942         if(box[0].size == 'md-right'){
37943             pos.push({
37944                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37945                 y : minY + (this.unitWidth + this.gutter) * 1
37946             });
37947             
37948             return pos;
37949         }
37950         
37951         var rand = Math.floor(Math.random() * (4 - box[0].y));
37952         
37953         pos.push({
37954             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37955             y : minY + (this.unitWidth + this.gutter) * rand
37956         });
37957         
37958         return pos;
37959         
37960     },
37961     
37962     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37963     {
37964         var pos = [];
37965         
37966         if(box[0].size == 'xs'){
37967             
37968             pos.push({
37969                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37970                 y : minY
37971             });
37972
37973             pos.push({
37974                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37975                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37976             });
37977             
37978             return pos;
37979             
37980         }
37981         
37982         pos.push({
37983             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37984             y : minY
37985         });
37986
37987         pos.push({
37988             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37989             y : minY + (this.unitWidth + this.gutter) * 2
37990         });
37991         
37992         return pos;
37993         
37994     },
37995     
37996     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37997     {
37998         var pos = [];
37999         
38000         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38001             
38002             pos.push({
38003                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38004                 y : minY
38005             });
38006
38007             pos.push({
38008                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38009                 y : minY + (this.unitWidth + this.gutter) * 1
38010             });
38011             
38012             pos.push({
38013                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38014                 y : minY + (this.unitWidth + this.gutter) * 2
38015             });
38016             
38017             return pos;
38018             
38019         }
38020         
38021         if(box[0].size == 'xs' && box[1].size == 'xs'){
38022             
38023             pos.push({
38024                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38025                 y : minY
38026             });
38027
38028             pos.push({
38029                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38030                 y : minY
38031             });
38032             
38033             pos.push({
38034                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38035                 y : minY + (this.unitWidth + this.gutter) * 1
38036             });
38037             
38038             return pos;
38039             
38040         }
38041         
38042         pos.push({
38043             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38044             y : minY
38045         });
38046
38047         pos.push({
38048             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38049             y : minY + (this.unitWidth + this.gutter) * 2
38050         });
38051
38052         pos.push({
38053             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38054             y : minY + (this.unitWidth + this.gutter) * 2
38055         });
38056             
38057         return pos;
38058         
38059     },
38060     
38061     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38062     {
38063         var pos = [];
38064         
38065         if(box[0].size == 'xs'){
38066             
38067             pos.push({
38068                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38069                 y : minY
38070             });
38071
38072             pos.push({
38073                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38074                 y : minY
38075             });
38076             
38077             pos.push({
38078                 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),
38079                 y : minY
38080             });
38081             
38082             pos.push({
38083                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38084                 y : minY + (this.unitWidth + this.gutter) * 1
38085             });
38086             
38087             return pos;
38088             
38089         }
38090         
38091         pos.push({
38092             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38093             y : minY
38094         });
38095         
38096         pos.push({
38097             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38098             y : minY + (this.unitWidth + this.gutter) * 2
38099         });
38100         
38101         pos.push({
38102             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38103             y : minY + (this.unitWidth + this.gutter) * 2
38104         });
38105         
38106         pos.push({
38107             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),
38108             y : minY + (this.unitWidth + this.gutter) * 2
38109         });
38110
38111         return pos;
38112         
38113     },
38114     
38115     /**
38116     * remove a Masonry Brick
38117     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38118     */
38119     removeBrick : function(brick_id)
38120     {
38121         if (!brick_id) {
38122             return;
38123         }
38124         
38125         for (var i = 0; i<this.bricks.length; i++) {
38126             if (this.bricks[i].id == brick_id) {
38127                 this.bricks.splice(i,1);
38128                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38129                 this.initial();
38130             }
38131         }
38132     },
38133     
38134     /**
38135     * adds a Masonry Brick
38136     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38137     */
38138     addBrick : function(cfg)
38139     {
38140         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38141         //this.register(cn);
38142         cn.parentId = this.id;
38143         cn.render(this.el);
38144         return cn;
38145     },
38146     
38147     /**
38148     * register a Masonry Brick
38149     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38150     */
38151     
38152     register : function(brick)
38153     {
38154         this.bricks.push(brick);
38155         brick.masonryId = this.id;
38156     },
38157     
38158     /**
38159     * clear all the Masonry Brick
38160     */
38161     clearAll : function()
38162     {
38163         this.bricks = [];
38164         //this.getChildContainer().dom.innerHTML = "";
38165         this.el.dom.innerHTML = '';
38166     },
38167     
38168     getSelected : function()
38169     {
38170         if (!this.selectedBrick) {
38171             return false;
38172         }
38173         
38174         return this.selectedBrick;
38175     }
38176 });
38177
38178 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38179     
38180     groups: {},
38181      /**
38182     * register a Masonry Layout
38183     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38184     */
38185     
38186     register : function(layout)
38187     {
38188         this.groups[layout.id] = layout;
38189     },
38190     /**
38191     * fetch a  Masonry Layout based on the masonry layout ID
38192     * @param {string} the masonry layout to add
38193     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38194     */
38195     
38196     get: function(layout_id) {
38197         if (typeof(this.groups[layout_id]) == 'undefined') {
38198             return false;
38199         }
38200         return this.groups[layout_id] ;
38201     }
38202     
38203     
38204     
38205 });
38206
38207  
38208
38209  /**
38210  *
38211  * This is based on 
38212  * http://masonry.desandro.com
38213  *
38214  * The idea is to render all the bricks based on vertical width...
38215  *
38216  * The original code extends 'outlayer' - we might need to use that....
38217  * 
38218  */
38219
38220
38221 /**
38222  * @class Roo.bootstrap.LayoutMasonryAuto
38223  * @extends Roo.bootstrap.Component
38224  * Bootstrap Layout Masonry class
38225  * 
38226  * @constructor
38227  * Create a new Element
38228  * @param {Object} config The config object
38229  */
38230
38231 Roo.bootstrap.LayoutMasonryAuto = function(config){
38232     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38233 };
38234
38235 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38236     
38237       /**
38238      * @cfg {Boolean} isFitWidth  - resize the width..
38239      */   
38240     isFitWidth : false,  // options..
38241     /**
38242      * @cfg {Boolean} isOriginLeft = left align?
38243      */   
38244     isOriginLeft : true,
38245     /**
38246      * @cfg {Boolean} isOriginTop = top align?
38247      */   
38248     isOriginTop : false,
38249     /**
38250      * @cfg {Boolean} isLayoutInstant = no animation?
38251      */   
38252     isLayoutInstant : false, // needed?
38253     /**
38254      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38255      */   
38256     isResizingContainer : true,
38257     /**
38258      * @cfg {Number} columnWidth  width of the columns 
38259      */   
38260     
38261     columnWidth : 0,
38262     
38263     /**
38264      * @cfg {Number} maxCols maximum number of columns
38265      */   
38266     
38267     maxCols: 0,
38268     /**
38269      * @cfg {Number} padHeight padding below box..
38270      */   
38271     
38272     padHeight : 10, 
38273     
38274     /**
38275      * @cfg {Boolean} isAutoInitial defalut true
38276      */   
38277     
38278     isAutoInitial : true, 
38279     
38280     // private?
38281     gutter : 0,
38282     
38283     containerWidth: 0,
38284     initialColumnWidth : 0,
38285     currentSize : null,
38286     
38287     colYs : null, // array.
38288     maxY : 0,
38289     padWidth: 10,
38290     
38291     
38292     tag: 'div',
38293     cls: '',
38294     bricks: null, //CompositeElement
38295     cols : 0, // array?
38296     // element : null, // wrapped now this.el
38297     _isLayoutInited : null, 
38298     
38299     
38300     getAutoCreate : function(){
38301         
38302         var cfg = {
38303             tag: this.tag,
38304             cls: 'blog-masonary-wrapper ' + this.cls,
38305             cn : {
38306                 cls : 'mas-boxes masonary'
38307             }
38308         };
38309         
38310         return cfg;
38311     },
38312     
38313     getChildContainer: function( )
38314     {
38315         if (this.boxesEl) {
38316             return this.boxesEl;
38317         }
38318         
38319         this.boxesEl = this.el.select('.mas-boxes').first();
38320         
38321         return this.boxesEl;
38322     },
38323     
38324     
38325     initEvents : function()
38326     {
38327         var _this = this;
38328         
38329         if(this.isAutoInitial){
38330             Roo.log('hook children rendered');
38331             this.on('childrenrendered', function() {
38332                 Roo.log('children rendered');
38333                 _this.initial();
38334             } ,this);
38335         }
38336         
38337     },
38338     
38339     initial : function()
38340     {
38341         this.reloadItems();
38342
38343         this.currentSize = this.el.getBox(true);
38344
38345         /// was window resize... - let's see if this works..
38346         Roo.EventManager.onWindowResize(this.resize, this); 
38347
38348         if(!this.isAutoInitial){
38349             this.layout();
38350             return;
38351         }
38352         
38353         this.layout.defer(500,this);
38354     },
38355     
38356     reloadItems: function()
38357     {
38358         this.bricks = this.el.select('.masonry-brick', true);
38359         
38360         this.bricks.each(function(b) {
38361             //Roo.log(b.getSize());
38362             if (!b.attr('originalwidth')) {
38363                 b.attr('originalwidth',  b.getSize().width);
38364             }
38365             
38366         });
38367         
38368         Roo.log(this.bricks.elements.length);
38369     },
38370     
38371     resize : function()
38372     {
38373         Roo.log('resize');
38374         var cs = this.el.getBox(true);
38375         
38376         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38377             Roo.log("no change in with or X");
38378             return;
38379         }
38380         this.currentSize = cs;
38381         this.layout();
38382     },
38383     
38384     layout : function()
38385     {
38386          Roo.log('layout');
38387         this._resetLayout();
38388         //this._manageStamps();
38389       
38390         // don't animate first layout
38391         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38392         this.layoutItems( isInstant );
38393       
38394         // flag for initalized
38395         this._isLayoutInited = true;
38396     },
38397     
38398     layoutItems : function( isInstant )
38399     {
38400         //var items = this._getItemsForLayout( this.items );
38401         // original code supports filtering layout items.. we just ignore it..
38402         
38403         this._layoutItems( this.bricks , isInstant );
38404       
38405         this._postLayout();
38406     },
38407     _layoutItems : function ( items , isInstant)
38408     {
38409        //this.fireEvent( 'layout', this, items );
38410     
38411
38412         if ( !items || !items.elements.length ) {
38413           // no items, emit event with empty array
38414             return;
38415         }
38416
38417         var queue = [];
38418         items.each(function(item) {
38419             Roo.log("layout item");
38420             Roo.log(item);
38421             // get x/y object from method
38422             var position = this._getItemLayoutPosition( item );
38423             // enqueue
38424             position.item = item;
38425             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38426             queue.push( position );
38427         }, this);
38428       
38429         this._processLayoutQueue( queue );
38430     },
38431     /** Sets position of item in DOM
38432     * @param {Element} item
38433     * @param {Number} x - horizontal position
38434     * @param {Number} y - vertical position
38435     * @param {Boolean} isInstant - disables transitions
38436     */
38437     _processLayoutQueue : function( queue )
38438     {
38439         for ( var i=0, len = queue.length; i < len; i++ ) {
38440             var obj = queue[i];
38441             obj.item.position('absolute');
38442             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38443         }
38444     },
38445       
38446     
38447     /**
38448     * Any logic you want to do after each layout,
38449     * i.e. size the container
38450     */
38451     _postLayout : function()
38452     {
38453         this.resizeContainer();
38454     },
38455     
38456     resizeContainer : function()
38457     {
38458         if ( !this.isResizingContainer ) {
38459             return;
38460         }
38461         var size = this._getContainerSize();
38462         if ( size ) {
38463             this.el.setSize(size.width,size.height);
38464             this.boxesEl.setSize(size.width,size.height);
38465         }
38466     },
38467     
38468     
38469     
38470     _resetLayout : function()
38471     {
38472         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38473         this.colWidth = this.el.getWidth();
38474         //this.gutter = this.el.getWidth(); 
38475         
38476         this.measureColumns();
38477
38478         // reset column Y
38479         var i = this.cols;
38480         this.colYs = [];
38481         while (i--) {
38482             this.colYs.push( 0 );
38483         }
38484     
38485         this.maxY = 0;
38486     },
38487
38488     measureColumns : function()
38489     {
38490         this.getContainerWidth();
38491       // if columnWidth is 0, default to outerWidth of first item
38492         if ( !this.columnWidth ) {
38493             var firstItem = this.bricks.first();
38494             Roo.log(firstItem);
38495             this.columnWidth  = this.containerWidth;
38496             if (firstItem && firstItem.attr('originalwidth') ) {
38497                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38498             }
38499             // columnWidth fall back to item of first element
38500             Roo.log("set column width?");
38501                         this.initialColumnWidth = this.columnWidth  ;
38502
38503             // if first elem has no width, default to size of container
38504             
38505         }
38506         
38507         
38508         if (this.initialColumnWidth) {
38509             this.columnWidth = this.initialColumnWidth;
38510         }
38511         
38512         
38513             
38514         // column width is fixed at the top - however if container width get's smaller we should
38515         // reduce it...
38516         
38517         // this bit calcs how man columns..
38518             
38519         var columnWidth = this.columnWidth += this.gutter;
38520       
38521         // calculate columns
38522         var containerWidth = this.containerWidth + this.gutter;
38523         
38524         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38525         // fix rounding errors, typically with gutters
38526         var excess = columnWidth - containerWidth % columnWidth;
38527         
38528         
38529         // if overshoot is less than a pixel, round up, otherwise floor it
38530         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38531         cols = Math[ mathMethod ]( cols );
38532         this.cols = Math.max( cols, 1 );
38533         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38534         
38535          // padding positioning..
38536         var totalColWidth = this.cols * this.columnWidth;
38537         var padavail = this.containerWidth - totalColWidth;
38538         // so for 2 columns - we need 3 'pads'
38539         
38540         var padNeeded = (1+this.cols) * this.padWidth;
38541         
38542         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38543         
38544         this.columnWidth += padExtra
38545         //this.padWidth = Math.floor(padavail /  ( this.cols));
38546         
38547         // adjust colum width so that padding is fixed??
38548         
38549         // we have 3 columns ... total = width * 3
38550         // we have X left over... that should be used by 
38551         
38552         //if (this.expandC) {
38553             
38554         //}
38555         
38556         
38557         
38558     },
38559     
38560     getContainerWidth : function()
38561     {
38562        /* // container is parent if fit width
38563         var container = this.isFitWidth ? this.element.parentNode : this.element;
38564         // check that this.size and size are there
38565         // IE8 triggers resize on body size change, so they might not be
38566         
38567         var size = getSize( container );  //FIXME
38568         this.containerWidth = size && size.innerWidth; //FIXME
38569         */
38570          
38571         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38572         
38573     },
38574     
38575     _getItemLayoutPosition : function( item )  // what is item?
38576     {
38577         // we resize the item to our columnWidth..
38578       
38579         item.setWidth(this.columnWidth);
38580         item.autoBoxAdjust  = false;
38581         
38582         var sz = item.getSize();
38583  
38584         // how many columns does this brick span
38585         var remainder = this.containerWidth % this.columnWidth;
38586         
38587         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38588         // round if off by 1 pixel, otherwise use ceil
38589         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38590         colSpan = Math.min( colSpan, this.cols );
38591         
38592         // normally this should be '1' as we dont' currently allow multi width columns..
38593         
38594         var colGroup = this._getColGroup( colSpan );
38595         // get the minimum Y value from the columns
38596         var minimumY = Math.min.apply( Math, colGroup );
38597         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38598         
38599         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38600          
38601         // position the brick
38602         var position = {
38603             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38604             y: this.currentSize.y + minimumY + this.padHeight
38605         };
38606         
38607         Roo.log(position);
38608         // apply setHeight to necessary columns
38609         var setHeight = minimumY + sz.height + this.padHeight;
38610         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38611         
38612         var setSpan = this.cols + 1 - colGroup.length;
38613         for ( var i = 0; i < setSpan; i++ ) {
38614           this.colYs[ shortColIndex + i ] = setHeight ;
38615         }
38616       
38617         return position;
38618     },
38619     
38620     /**
38621      * @param {Number} colSpan - number of columns the element spans
38622      * @returns {Array} colGroup
38623      */
38624     _getColGroup : function( colSpan )
38625     {
38626         if ( colSpan < 2 ) {
38627           // if brick spans only one column, use all the column Ys
38628           return this.colYs;
38629         }
38630       
38631         var colGroup = [];
38632         // how many different places could this brick fit horizontally
38633         var groupCount = this.cols + 1 - colSpan;
38634         // for each group potential horizontal position
38635         for ( var i = 0; i < groupCount; i++ ) {
38636           // make an array of colY values for that one group
38637           var groupColYs = this.colYs.slice( i, i + colSpan );
38638           // and get the max value of the array
38639           colGroup[i] = Math.max.apply( Math, groupColYs );
38640         }
38641         return colGroup;
38642     },
38643     /*
38644     _manageStamp : function( stamp )
38645     {
38646         var stampSize =  stamp.getSize();
38647         var offset = stamp.getBox();
38648         // get the columns that this stamp affects
38649         var firstX = this.isOriginLeft ? offset.x : offset.right;
38650         var lastX = firstX + stampSize.width;
38651         var firstCol = Math.floor( firstX / this.columnWidth );
38652         firstCol = Math.max( 0, firstCol );
38653         
38654         var lastCol = Math.floor( lastX / this.columnWidth );
38655         // lastCol should not go over if multiple of columnWidth #425
38656         lastCol -= lastX % this.columnWidth ? 0 : 1;
38657         lastCol = Math.min( this.cols - 1, lastCol );
38658         
38659         // set colYs to bottom of the stamp
38660         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38661             stampSize.height;
38662             
38663         for ( var i = firstCol; i <= lastCol; i++ ) {
38664           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38665         }
38666     },
38667     */
38668     
38669     _getContainerSize : function()
38670     {
38671         this.maxY = Math.max.apply( Math, this.colYs );
38672         var size = {
38673             height: this.maxY
38674         };
38675       
38676         if ( this.isFitWidth ) {
38677             size.width = this._getContainerFitWidth();
38678         }
38679       
38680         return size;
38681     },
38682     
38683     _getContainerFitWidth : function()
38684     {
38685         var unusedCols = 0;
38686         // count unused columns
38687         var i = this.cols;
38688         while ( --i ) {
38689           if ( this.colYs[i] !== 0 ) {
38690             break;
38691           }
38692           unusedCols++;
38693         }
38694         // fit container to columns that have been used
38695         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38696     },
38697     
38698     needsResizeLayout : function()
38699     {
38700         var previousWidth = this.containerWidth;
38701         this.getContainerWidth();
38702         return previousWidth !== this.containerWidth;
38703     }
38704  
38705 });
38706
38707  
38708
38709  /*
38710  * - LGPL
38711  *
38712  * element
38713  * 
38714  */
38715
38716 /**
38717  * @class Roo.bootstrap.MasonryBrick
38718  * @extends Roo.bootstrap.Component
38719  * Bootstrap MasonryBrick class
38720  * 
38721  * @constructor
38722  * Create a new MasonryBrick
38723  * @param {Object} config The config object
38724  */
38725
38726 Roo.bootstrap.MasonryBrick = function(config){
38727     
38728     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38729     
38730     Roo.bootstrap.MasonryBrick.register(this);
38731     
38732     this.addEvents({
38733         // raw events
38734         /**
38735          * @event click
38736          * When a MasonryBrick is clcik
38737          * @param {Roo.bootstrap.MasonryBrick} this
38738          * @param {Roo.EventObject} e
38739          */
38740         "click" : true
38741     });
38742 };
38743
38744 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38745     
38746     /**
38747      * @cfg {String} title
38748      */   
38749     title : '',
38750     /**
38751      * @cfg {String} html
38752      */   
38753     html : '',
38754     /**
38755      * @cfg {String} bgimage
38756      */   
38757     bgimage : '',
38758     /**
38759      * @cfg {String} videourl
38760      */   
38761     videourl : '',
38762     /**
38763      * @cfg {String} cls
38764      */   
38765     cls : '',
38766     /**
38767      * @cfg {String} href
38768      */   
38769     href : '',
38770     /**
38771      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38772      */   
38773     size : 'xs',
38774     
38775     /**
38776      * @cfg {String} placetitle (center|bottom)
38777      */   
38778     placetitle : '',
38779     
38780     /**
38781      * @cfg {Boolean} isFitContainer defalut true
38782      */   
38783     isFitContainer : true, 
38784     
38785     /**
38786      * @cfg {Boolean} preventDefault defalut false
38787      */   
38788     preventDefault : false, 
38789     
38790     /**
38791      * @cfg {Boolean} inverse defalut false
38792      */   
38793     maskInverse : false, 
38794     
38795     getAutoCreate : function()
38796     {
38797         if(!this.isFitContainer){
38798             return this.getSplitAutoCreate();
38799         }
38800         
38801         var cls = 'masonry-brick masonry-brick-full';
38802         
38803         if(this.href.length){
38804             cls += ' masonry-brick-link';
38805         }
38806         
38807         if(this.bgimage.length){
38808             cls += ' masonry-brick-image';
38809         }
38810         
38811         if(this.maskInverse){
38812             cls += ' mask-inverse';
38813         }
38814         
38815         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38816             cls += ' enable-mask';
38817         }
38818         
38819         if(this.size){
38820             cls += ' masonry-' + this.size + '-brick';
38821         }
38822         
38823         if(this.placetitle.length){
38824             
38825             switch (this.placetitle) {
38826                 case 'center' :
38827                     cls += ' masonry-center-title';
38828                     break;
38829                 case 'bottom' :
38830                     cls += ' masonry-bottom-title';
38831                     break;
38832                 default:
38833                     break;
38834             }
38835             
38836         } else {
38837             if(!this.html.length && !this.bgimage.length){
38838                 cls += ' masonry-center-title';
38839             }
38840
38841             if(!this.html.length && this.bgimage.length){
38842                 cls += ' masonry-bottom-title';
38843             }
38844         }
38845         
38846         if(this.cls){
38847             cls += ' ' + this.cls;
38848         }
38849         
38850         var cfg = {
38851             tag: (this.href.length) ? 'a' : 'div',
38852             cls: cls,
38853             cn: [
38854                 {
38855                     tag: 'div',
38856                     cls: 'masonry-brick-mask'
38857                 },
38858                 {
38859                     tag: 'div',
38860                     cls: 'masonry-brick-paragraph',
38861                     cn: []
38862                 }
38863             ]
38864         };
38865         
38866         if(this.href.length){
38867             cfg.href = this.href;
38868         }
38869         
38870         var cn = cfg.cn[1].cn;
38871         
38872         if(this.title.length){
38873             cn.push({
38874                 tag: 'h4',
38875                 cls: 'masonry-brick-title',
38876                 html: this.title
38877             });
38878         }
38879         
38880         if(this.html.length){
38881             cn.push({
38882                 tag: 'p',
38883                 cls: 'masonry-brick-text',
38884                 html: this.html
38885             });
38886         }
38887         
38888         if (!this.title.length && !this.html.length) {
38889             cfg.cn[1].cls += ' hide';
38890         }
38891         
38892         if(this.bgimage.length){
38893             cfg.cn.push({
38894                 tag: 'img',
38895                 cls: 'masonry-brick-image-view',
38896                 src: this.bgimage
38897             });
38898         }
38899         
38900         if(this.videourl.length){
38901             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38902             // youtube support only?
38903             cfg.cn.push({
38904                 tag: 'iframe',
38905                 cls: 'masonry-brick-image-view',
38906                 src: vurl,
38907                 frameborder : 0,
38908                 allowfullscreen : true
38909             });
38910         }
38911         
38912         return cfg;
38913         
38914     },
38915     
38916     getSplitAutoCreate : function()
38917     {
38918         var cls = 'masonry-brick masonry-brick-split';
38919         
38920         if(this.href.length){
38921             cls += ' masonry-brick-link';
38922         }
38923         
38924         if(this.bgimage.length){
38925             cls += ' masonry-brick-image';
38926         }
38927         
38928         if(this.size){
38929             cls += ' masonry-' + this.size + '-brick';
38930         }
38931         
38932         switch (this.placetitle) {
38933             case 'center' :
38934                 cls += ' masonry-center-title';
38935                 break;
38936             case 'bottom' :
38937                 cls += ' masonry-bottom-title';
38938                 break;
38939             default:
38940                 if(!this.bgimage.length){
38941                     cls += ' masonry-center-title';
38942                 }
38943
38944                 if(this.bgimage.length){
38945                     cls += ' masonry-bottom-title';
38946                 }
38947                 break;
38948         }
38949         
38950         if(this.cls){
38951             cls += ' ' + this.cls;
38952         }
38953         
38954         var cfg = {
38955             tag: (this.href.length) ? 'a' : 'div',
38956             cls: cls,
38957             cn: [
38958                 {
38959                     tag: 'div',
38960                     cls: 'masonry-brick-split-head',
38961                     cn: [
38962                         {
38963                             tag: 'div',
38964                             cls: 'masonry-brick-paragraph',
38965                             cn: []
38966                         }
38967                     ]
38968                 },
38969                 {
38970                     tag: 'div',
38971                     cls: 'masonry-brick-split-body',
38972                     cn: []
38973                 }
38974             ]
38975         };
38976         
38977         if(this.href.length){
38978             cfg.href = this.href;
38979         }
38980         
38981         if(this.title.length){
38982             cfg.cn[0].cn[0].cn.push({
38983                 tag: 'h4',
38984                 cls: 'masonry-brick-title',
38985                 html: this.title
38986             });
38987         }
38988         
38989         if(this.html.length){
38990             cfg.cn[1].cn.push({
38991                 tag: 'p',
38992                 cls: 'masonry-brick-text',
38993                 html: this.html
38994             });
38995         }
38996
38997         if(this.bgimage.length){
38998             cfg.cn[0].cn.push({
38999                 tag: 'img',
39000                 cls: 'masonry-brick-image-view',
39001                 src: this.bgimage
39002             });
39003         }
39004         
39005         if(this.videourl.length){
39006             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39007             // youtube support only?
39008             cfg.cn[0].cn.cn.push({
39009                 tag: 'iframe',
39010                 cls: 'masonry-brick-image-view',
39011                 src: vurl,
39012                 frameborder : 0,
39013                 allowfullscreen : true
39014             });
39015         }
39016         
39017         return cfg;
39018     },
39019     
39020     initEvents: function() 
39021     {
39022         switch (this.size) {
39023             case 'xs' :
39024                 this.x = 1;
39025                 this.y = 1;
39026                 break;
39027             case 'sm' :
39028                 this.x = 2;
39029                 this.y = 2;
39030                 break;
39031             case 'md' :
39032             case 'md-left' :
39033             case 'md-right' :
39034                 this.x = 3;
39035                 this.y = 3;
39036                 break;
39037             case 'tall' :
39038                 this.x = 2;
39039                 this.y = 3;
39040                 break;
39041             case 'wide' :
39042                 this.x = 3;
39043                 this.y = 2;
39044                 break;
39045             case 'wide-thin' :
39046                 this.x = 3;
39047                 this.y = 1;
39048                 break;
39049                         
39050             default :
39051                 break;
39052         }
39053         
39054         if(Roo.isTouch){
39055             this.el.on('touchstart', this.onTouchStart, this);
39056             this.el.on('touchmove', this.onTouchMove, this);
39057             this.el.on('touchend', this.onTouchEnd, this);
39058             this.el.on('contextmenu', this.onContextMenu, this);
39059         } else {
39060             this.el.on('mouseenter'  ,this.enter, this);
39061             this.el.on('mouseleave', this.leave, this);
39062             this.el.on('click', this.onClick, this);
39063         }
39064         
39065         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39066             this.parent().bricks.push(this);   
39067         }
39068         
39069     },
39070     
39071     onClick: function(e, el)
39072     {
39073         var time = this.endTimer - this.startTimer;
39074         // Roo.log(e.preventDefault());
39075         if(Roo.isTouch){
39076             if(time > 1000){
39077                 e.preventDefault();
39078                 return;
39079             }
39080         }
39081         
39082         if(!this.preventDefault){
39083             return;
39084         }
39085         
39086         e.preventDefault();
39087         
39088         if (this.activeClass != '') {
39089             this.selectBrick();
39090         }
39091         
39092         this.fireEvent('click', this, e);
39093     },
39094     
39095     enter: function(e, el)
39096     {
39097         e.preventDefault();
39098         
39099         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39100             return;
39101         }
39102         
39103         if(this.bgimage.length && this.html.length){
39104             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39105         }
39106     },
39107     
39108     leave: function(e, el)
39109     {
39110         e.preventDefault();
39111         
39112         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39113             return;
39114         }
39115         
39116         if(this.bgimage.length && this.html.length){
39117             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39118         }
39119     },
39120     
39121     onTouchStart: function(e, el)
39122     {
39123 //        e.preventDefault();
39124         
39125         this.touchmoved = false;
39126         
39127         if(!this.isFitContainer){
39128             return;
39129         }
39130         
39131         if(!this.bgimage.length || !this.html.length){
39132             return;
39133         }
39134         
39135         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39136         
39137         this.timer = new Date().getTime();
39138         
39139     },
39140     
39141     onTouchMove: function(e, el)
39142     {
39143         this.touchmoved = true;
39144     },
39145     
39146     onContextMenu : function(e,el)
39147     {
39148         e.preventDefault();
39149         e.stopPropagation();
39150         return false;
39151     },
39152     
39153     onTouchEnd: function(e, el)
39154     {
39155 //        e.preventDefault();
39156         
39157         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39158         
39159             this.leave(e,el);
39160             
39161             return;
39162         }
39163         
39164         if(!this.bgimage.length || !this.html.length){
39165             
39166             if(this.href.length){
39167                 window.location.href = this.href;
39168             }
39169             
39170             return;
39171         }
39172         
39173         if(!this.isFitContainer){
39174             return;
39175         }
39176         
39177         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39178         
39179         window.location.href = this.href;
39180     },
39181     
39182     //selection on single brick only
39183     selectBrick : function() {
39184         
39185         if (!this.parentId) {
39186             return;
39187         }
39188         
39189         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39190         var index = m.selectedBrick.indexOf(this.id);
39191         
39192         if ( index > -1) {
39193             m.selectedBrick.splice(index,1);
39194             this.el.removeClass(this.activeClass);
39195             return;
39196         }
39197         
39198         for(var i = 0; i < m.selectedBrick.length; i++) {
39199             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39200             b.el.removeClass(b.activeClass);
39201         }
39202         
39203         m.selectedBrick = [];
39204         
39205         m.selectedBrick.push(this.id);
39206         this.el.addClass(this.activeClass);
39207         return;
39208     },
39209     
39210     isSelected : function(){
39211         return this.el.hasClass(this.activeClass);
39212         
39213     }
39214 });
39215
39216 Roo.apply(Roo.bootstrap.MasonryBrick, {
39217     
39218     //groups: {},
39219     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39220      /**
39221     * register a Masonry Brick
39222     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39223     */
39224     
39225     register : function(brick)
39226     {
39227         //this.groups[brick.id] = brick;
39228         this.groups.add(brick.id, brick);
39229     },
39230     /**
39231     * fetch a  masonry brick based on the masonry brick ID
39232     * @param {string} the masonry brick to add
39233     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39234     */
39235     
39236     get: function(brick_id) 
39237     {
39238         // if (typeof(this.groups[brick_id]) == 'undefined') {
39239         //     return false;
39240         // }
39241         // return this.groups[brick_id] ;
39242         
39243         if(this.groups.key(brick_id)) {
39244             return this.groups.key(brick_id);
39245         }
39246         
39247         return false;
39248     }
39249     
39250     
39251     
39252 });
39253
39254  /*
39255  * - LGPL
39256  *
39257  * element
39258  * 
39259  */
39260
39261 /**
39262  * @class Roo.bootstrap.Brick
39263  * @extends Roo.bootstrap.Component
39264  * Bootstrap Brick class
39265  * 
39266  * @constructor
39267  * Create a new Brick
39268  * @param {Object} config The config object
39269  */
39270
39271 Roo.bootstrap.Brick = function(config){
39272     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39273     
39274     this.addEvents({
39275         // raw events
39276         /**
39277          * @event click
39278          * When a Brick is click
39279          * @param {Roo.bootstrap.Brick} this
39280          * @param {Roo.EventObject} e
39281          */
39282         "click" : true
39283     });
39284 };
39285
39286 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39287     
39288     /**
39289      * @cfg {String} title
39290      */   
39291     title : '',
39292     /**
39293      * @cfg {String} html
39294      */   
39295     html : '',
39296     /**
39297      * @cfg {String} bgimage
39298      */   
39299     bgimage : '',
39300     /**
39301      * @cfg {String} cls
39302      */   
39303     cls : '',
39304     /**
39305      * @cfg {String} href
39306      */   
39307     href : '',
39308     /**
39309      * @cfg {String} video
39310      */   
39311     video : '',
39312     /**
39313      * @cfg {Boolean} square
39314      */   
39315     square : true,
39316     
39317     getAutoCreate : function()
39318     {
39319         var cls = 'roo-brick';
39320         
39321         if(this.href.length){
39322             cls += ' roo-brick-link';
39323         }
39324         
39325         if(this.bgimage.length){
39326             cls += ' roo-brick-image';
39327         }
39328         
39329         if(!this.html.length && !this.bgimage.length){
39330             cls += ' roo-brick-center-title';
39331         }
39332         
39333         if(!this.html.length && this.bgimage.length){
39334             cls += ' roo-brick-bottom-title';
39335         }
39336         
39337         if(this.cls){
39338             cls += ' ' + this.cls;
39339         }
39340         
39341         var cfg = {
39342             tag: (this.href.length) ? 'a' : 'div',
39343             cls: cls,
39344             cn: [
39345                 {
39346                     tag: 'div',
39347                     cls: 'roo-brick-paragraph',
39348                     cn: []
39349                 }
39350             ]
39351         };
39352         
39353         if(this.href.length){
39354             cfg.href = this.href;
39355         }
39356         
39357         var cn = cfg.cn[0].cn;
39358         
39359         if(this.title.length){
39360             cn.push({
39361                 tag: 'h4',
39362                 cls: 'roo-brick-title',
39363                 html: this.title
39364             });
39365         }
39366         
39367         if(this.html.length){
39368             cn.push({
39369                 tag: 'p',
39370                 cls: 'roo-brick-text',
39371                 html: this.html
39372             });
39373         } else {
39374             cn.cls += ' hide';
39375         }
39376         
39377         if(this.bgimage.length){
39378             cfg.cn.push({
39379                 tag: 'img',
39380                 cls: 'roo-brick-image-view',
39381                 src: this.bgimage
39382             });
39383         }
39384         
39385         return cfg;
39386     },
39387     
39388     initEvents: function() 
39389     {
39390         if(this.title.length || this.html.length){
39391             this.el.on('mouseenter'  ,this.enter, this);
39392             this.el.on('mouseleave', this.leave, this);
39393         }
39394         
39395         Roo.EventManager.onWindowResize(this.resize, this); 
39396         
39397         if(this.bgimage.length){
39398             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39399             this.imageEl.on('load', this.onImageLoad, this);
39400             return;
39401         }
39402         
39403         this.resize();
39404     },
39405     
39406     onImageLoad : function()
39407     {
39408         this.resize();
39409     },
39410     
39411     resize : function()
39412     {
39413         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39414         
39415         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39416         
39417         if(this.bgimage.length){
39418             var image = this.el.select('.roo-brick-image-view', true).first();
39419             
39420             image.setWidth(paragraph.getWidth());
39421             
39422             if(this.square){
39423                 image.setHeight(paragraph.getWidth());
39424             }
39425             
39426             this.el.setHeight(image.getHeight());
39427             paragraph.setHeight(image.getHeight());
39428             
39429         }
39430         
39431     },
39432     
39433     enter: function(e, el)
39434     {
39435         e.preventDefault();
39436         
39437         if(this.bgimage.length){
39438             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39439             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39440         }
39441     },
39442     
39443     leave: function(e, el)
39444     {
39445         e.preventDefault();
39446         
39447         if(this.bgimage.length){
39448             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39449             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39450         }
39451     }
39452     
39453 });
39454
39455  
39456
39457  /*
39458  * - LGPL
39459  *
39460  * Number field 
39461  */
39462
39463 /**
39464  * @class Roo.bootstrap.form.NumberField
39465  * @extends Roo.bootstrap.form.Input
39466  * Bootstrap NumberField class
39467  * 
39468  * 
39469  * 
39470  * 
39471  * @constructor
39472  * Create a new NumberField
39473  * @param {Object} config The config object
39474  */
39475
39476 Roo.bootstrap.form.NumberField = function(config){
39477     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39478 };
39479
39480 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39481     
39482     /**
39483      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39484      */
39485     allowDecimals : true,
39486     /**
39487      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39488      */
39489     decimalSeparator : ".",
39490     /**
39491      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39492      */
39493     decimalPrecision : 2,
39494     /**
39495      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39496      */
39497     allowNegative : true,
39498     
39499     /**
39500      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39501      */
39502     allowZero: true,
39503     /**
39504      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39505      */
39506     minValue : Number.NEGATIVE_INFINITY,
39507     /**
39508      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39509      */
39510     maxValue : Number.MAX_VALUE,
39511     /**
39512      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39513      */
39514     minText : "The minimum value for this field is {0}",
39515     /**
39516      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39517      */
39518     maxText : "The maximum value for this field is {0}",
39519     /**
39520      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39521      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39522      */
39523     nanText : "{0} is not a valid number",
39524     /**
39525      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39526      */
39527     thousandsDelimiter : false,
39528     /**
39529      * @cfg {String} valueAlign alignment of value
39530      */
39531     valueAlign : "left",
39532
39533     getAutoCreate : function()
39534     {
39535         var hiddenInput = {
39536             tag: 'input',
39537             type: 'hidden',
39538             id: Roo.id(),
39539             cls: 'hidden-number-input'
39540         };
39541         
39542         if (this.name) {
39543             hiddenInput.name = this.name;
39544         }
39545         
39546         this.name = '';
39547         
39548         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39549         
39550         this.name = hiddenInput.name;
39551         
39552         if(cfg.cn.length > 0) {
39553             cfg.cn.push(hiddenInput);
39554         }
39555         
39556         return cfg;
39557     },
39558
39559     // private
39560     initEvents : function()
39561     {   
39562         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39563         
39564         var allowed = "0123456789";
39565         
39566         if(this.allowDecimals){
39567             allowed += this.decimalSeparator;
39568         }
39569         
39570         if(this.allowNegative){
39571             allowed += "-";
39572         }
39573         
39574         if(this.thousandsDelimiter) {
39575             allowed += ",";
39576         }
39577         
39578         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39579         
39580         var keyPress = function(e){
39581             
39582             var k = e.getKey();
39583             
39584             var c = e.getCharCode();
39585             
39586             if(
39587                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39588                     allowed.indexOf(String.fromCharCode(c)) === -1
39589             ){
39590                 e.stopEvent();
39591                 return;
39592             }
39593             
39594             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39595                 return;
39596             }
39597             
39598             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39599                 e.stopEvent();
39600             }
39601         };
39602         
39603         this.el.on("keypress", keyPress, this);
39604     },
39605     
39606     validateValue : function(value)
39607     {
39608         
39609         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39610             return false;
39611         }
39612         
39613         var num = this.parseValue(value);
39614         
39615         if(isNaN(num)){
39616             this.markInvalid(String.format(this.nanText, value));
39617             return false;
39618         }
39619         
39620         if(num < this.minValue){
39621             this.markInvalid(String.format(this.minText, this.minValue));
39622             return false;
39623         }
39624         
39625         if(num > this.maxValue){
39626             this.markInvalid(String.format(this.maxText, this.maxValue));
39627             return false;
39628         }
39629         
39630         return true;
39631     },
39632
39633     getValue : function()
39634     {
39635         var v = this.hiddenEl().getValue();
39636         
39637         return this.fixPrecision(this.parseValue(v));
39638     },
39639
39640     parseValue : function(value)
39641     {
39642         if(this.thousandsDelimiter) {
39643             value += "";
39644             r = new RegExp(",", "g");
39645             value = value.replace(r, "");
39646         }
39647         
39648         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39649         return isNaN(value) ? '' : value;
39650     },
39651
39652     fixPrecision : function(value)
39653     {
39654         if(this.thousandsDelimiter) {
39655             value += "";
39656             r = new RegExp(",", "g");
39657             value = value.replace(r, "");
39658         }
39659         
39660         var nan = isNaN(value);
39661         
39662         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39663             return nan ? '' : value;
39664         }
39665         return parseFloat(value).toFixed(this.decimalPrecision);
39666     },
39667
39668     setValue : function(v)
39669     {
39670         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39671         
39672         this.value = v;
39673         
39674         if(this.rendered){
39675             
39676             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39677             
39678             this.inputEl().dom.value = (v == '') ? '' :
39679                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39680             
39681             if(!this.allowZero && v === '0') {
39682                 this.hiddenEl().dom.value = '';
39683                 this.inputEl().dom.value = '';
39684             }
39685             
39686             this.validate();
39687         }
39688     },
39689
39690     decimalPrecisionFcn : function(v)
39691     {
39692         return Math.floor(v);
39693     },
39694
39695     beforeBlur : function()
39696     {
39697         var v = this.parseValue(this.getRawValue());
39698         
39699         if(v || v === 0 || v === ''){
39700             this.setValue(v);
39701         }
39702     },
39703     
39704     hiddenEl : function()
39705     {
39706         return this.el.select('input.hidden-number-input',true).first();
39707     }
39708     
39709 });
39710
39711  
39712
39713 /*
39714 * Licence: LGPL
39715 */
39716
39717 /**
39718  * @class Roo.bootstrap.DocumentSlider
39719  * @extends Roo.bootstrap.Component
39720  * Bootstrap DocumentSlider class
39721  * 
39722  * @constructor
39723  * Create a new DocumentViewer
39724  * @param {Object} config The config object
39725  */
39726
39727 Roo.bootstrap.DocumentSlider = function(config){
39728     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39729     
39730     this.files = [];
39731     
39732     this.addEvents({
39733         /**
39734          * @event initial
39735          * Fire after initEvent
39736          * @param {Roo.bootstrap.DocumentSlider} this
39737          */
39738         "initial" : true,
39739         /**
39740          * @event update
39741          * Fire after update
39742          * @param {Roo.bootstrap.DocumentSlider} this
39743          */
39744         "update" : true,
39745         /**
39746          * @event click
39747          * Fire after click
39748          * @param {Roo.bootstrap.DocumentSlider} this
39749          */
39750         "click" : true
39751     });
39752 };
39753
39754 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39755     
39756     files : false,
39757     
39758     indicator : 0,
39759     
39760     getAutoCreate : function()
39761     {
39762         var cfg = {
39763             tag : 'div',
39764             cls : 'roo-document-slider',
39765             cn : [
39766                 {
39767                     tag : 'div',
39768                     cls : 'roo-document-slider-header',
39769                     cn : [
39770                         {
39771                             tag : 'div',
39772                             cls : 'roo-document-slider-header-title'
39773                         }
39774                     ]
39775                 },
39776                 {
39777                     tag : 'div',
39778                     cls : 'roo-document-slider-body',
39779                     cn : [
39780                         {
39781                             tag : 'div',
39782                             cls : 'roo-document-slider-prev',
39783                             cn : [
39784                                 {
39785                                     tag : 'i',
39786                                     cls : 'fa fa-chevron-left'
39787                                 }
39788                             ]
39789                         },
39790                         {
39791                             tag : 'div',
39792                             cls : 'roo-document-slider-thumb',
39793                             cn : [
39794                                 {
39795                                     tag : 'img',
39796                                     cls : 'roo-document-slider-image'
39797                                 }
39798                             ]
39799                         },
39800                         {
39801                             tag : 'div',
39802                             cls : 'roo-document-slider-next',
39803                             cn : [
39804                                 {
39805                                     tag : 'i',
39806                                     cls : 'fa fa-chevron-right'
39807                                 }
39808                             ]
39809                         }
39810                     ]
39811                 }
39812             ]
39813         };
39814         
39815         return cfg;
39816     },
39817     
39818     initEvents : function()
39819     {
39820         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39821         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39822         
39823         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39824         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39825         
39826         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39827         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39828         
39829         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39830         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39831         
39832         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39833         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39834         
39835         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39836         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39837         
39838         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39839         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39840         
39841         this.thumbEl.on('click', this.onClick, this);
39842         
39843         this.prevIndicator.on('click', this.prev, this);
39844         
39845         this.nextIndicator.on('click', this.next, this);
39846         
39847     },
39848     
39849     initial : function()
39850     {
39851         if(this.files.length){
39852             this.indicator = 1;
39853             this.update()
39854         }
39855         
39856         this.fireEvent('initial', this);
39857     },
39858     
39859     update : function()
39860     {
39861         this.imageEl.attr('src', this.files[this.indicator - 1]);
39862         
39863         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39864         
39865         this.prevIndicator.show();
39866         
39867         if(this.indicator == 1){
39868             this.prevIndicator.hide();
39869         }
39870         
39871         this.nextIndicator.show();
39872         
39873         if(this.indicator == this.files.length){
39874             this.nextIndicator.hide();
39875         }
39876         
39877         this.thumbEl.scrollTo('top');
39878         
39879         this.fireEvent('update', this);
39880     },
39881     
39882     onClick : function(e)
39883     {
39884         e.preventDefault();
39885         
39886         this.fireEvent('click', this);
39887     },
39888     
39889     prev : function(e)
39890     {
39891         e.preventDefault();
39892         
39893         this.indicator = Math.max(1, this.indicator - 1);
39894         
39895         this.update();
39896     },
39897     
39898     next : function(e)
39899     {
39900         e.preventDefault();
39901         
39902         this.indicator = Math.min(this.files.length, this.indicator + 1);
39903         
39904         this.update();
39905     }
39906 });
39907 /*
39908  * - LGPL
39909  *
39910  * RadioSet
39911  *
39912  *
39913  */
39914
39915 /**
39916  * @class Roo.bootstrap.form.RadioSet
39917  * @extends Roo.bootstrap.form.Input
39918  * @children Roo.bootstrap.form.Radio
39919  * Bootstrap RadioSet class
39920  * @cfg {String} indicatorpos (left|right) default left
39921  * @cfg {Boolean} inline (true|false) inline the element (default true)
39922  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39923  * @constructor
39924  * Create a new RadioSet
39925  * @param {Object} config The config object
39926  */
39927
39928 Roo.bootstrap.form.RadioSet = function(config){
39929     
39930     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39931     
39932     this.radioes = [];
39933     
39934     Roo.bootstrap.form.RadioSet.register(this);
39935     
39936     this.addEvents({
39937         /**
39938         * @event check
39939         * Fires when the element is checked or unchecked.
39940         * @param {Roo.bootstrap.form.RadioSet} this This radio
39941         * @param {Roo.bootstrap.form.Radio} item The checked item
39942         */
39943        check : true,
39944        /**
39945         * @event click
39946         * Fires when the element is click.
39947         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39948         * @param {Roo.bootstrap.form.Radio} item The checked item
39949         * @param {Roo.EventObject} e The event object
39950         */
39951        click : true
39952     });
39953     
39954 };
39955
39956 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39957
39958     radioes : false,
39959     
39960     inline : true,
39961     
39962     weight : '',
39963     
39964     indicatorpos : 'left',
39965     
39966     getAutoCreate : function()
39967     {
39968         var label = {
39969             tag : 'label',
39970             cls : 'roo-radio-set-label',
39971             cn : [
39972                 {
39973                     tag : 'span',
39974                     html : this.fieldLabel
39975                 }
39976             ]
39977         };
39978         if (Roo.bootstrap.version == 3) {
39979             
39980             
39981             if(this.indicatorpos == 'left'){
39982                 label.cn.unshift({
39983                     tag : 'i',
39984                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39985                     tooltip : 'This field is required'
39986                 });
39987             } else {
39988                 label.cn.push({
39989                     tag : 'i',
39990                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39991                     tooltip : 'This field is required'
39992                 });
39993             }
39994         }
39995         var items = {
39996             tag : 'div',
39997             cls : 'roo-radio-set-items'
39998         };
39999         
40000         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40001         
40002         if (align === 'left' && this.fieldLabel.length) {
40003             
40004             items = {
40005                 cls : "roo-radio-set-right", 
40006                 cn: [
40007                     items
40008                 ]
40009             };
40010             
40011             if(this.labelWidth > 12){
40012                 label.style = "width: " + this.labelWidth + 'px';
40013             }
40014             
40015             if(this.labelWidth < 13 && this.labelmd == 0){
40016                 this.labelmd = this.labelWidth;
40017             }
40018             
40019             if(this.labellg > 0){
40020                 label.cls += ' col-lg-' + this.labellg;
40021                 items.cls += ' col-lg-' + (12 - this.labellg);
40022             }
40023             
40024             if(this.labelmd > 0){
40025                 label.cls += ' col-md-' + this.labelmd;
40026                 items.cls += ' col-md-' + (12 - this.labelmd);
40027             }
40028             
40029             if(this.labelsm > 0){
40030                 label.cls += ' col-sm-' + this.labelsm;
40031                 items.cls += ' col-sm-' + (12 - this.labelsm);
40032             }
40033             
40034             if(this.labelxs > 0){
40035                 label.cls += ' col-xs-' + this.labelxs;
40036                 items.cls += ' col-xs-' + (12 - this.labelxs);
40037             }
40038         }
40039         
40040         var cfg = {
40041             tag : 'div',
40042             cls : 'roo-radio-set',
40043             cn : [
40044                 {
40045                     tag : 'input',
40046                     cls : 'roo-radio-set-input',
40047                     type : 'hidden',
40048                     name : this.name,
40049                     value : this.value ? this.value :  ''
40050                 },
40051                 label,
40052                 items
40053             ]
40054         };
40055         
40056         if(this.weight.length){
40057             cfg.cls += ' roo-radio-' + this.weight;
40058         }
40059         
40060         if(this.inline) {
40061             cfg.cls += ' roo-radio-set-inline';
40062         }
40063         
40064         var settings=this;
40065         ['xs','sm','md','lg'].map(function(size){
40066             if (settings[size]) {
40067                 cfg.cls += ' col-' + size + '-' + settings[size];
40068             }
40069         });
40070         
40071         return cfg;
40072         
40073     },
40074
40075     initEvents : function()
40076     {
40077         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40078         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40079         
40080         if(!this.fieldLabel.length){
40081             this.labelEl.hide();
40082         }
40083         
40084         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40085         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40086         
40087         this.indicator = this.indicatorEl();
40088         
40089         if(this.indicator){
40090             this.indicator.addClass('invisible');
40091         }
40092         
40093         this.originalValue = this.getValue();
40094         
40095     },
40096     
40097     inputEl: function ()
40098     {
40099         return this.el.select('.roo-radio-set-input', true).first();
40100     },
40101     
40102     getChildContainer : function()
40103     {
40104         return this.itemsEl;
40105     },
40106     
40107     register : function(item)
40108     {
40109         this.radioes.push(item);
40110         
40111     },
40112     
40113     validate : function()
40114     {   
40115         if(this.getVisibilityEl().hasClass('hidden')){
40116             return true;
40117         }
40118         
40119         var valid = false;
40120         
40121         Roo.each(this.radioes, function(i){
40122             if(!i.checked){
40123                 return;
40124             }
40125             
40126             valid = true;
40127             return false;
40128         });
40129         
40130         if(this.allowBlank) {
40131             return true;
40132         }
40133         
40134         if(this.disabled || valid){
40135             this.markValid();
40136             return true;
40137         }
40138         
40139         this.markInvalid();
40140         return false;
40141         
40142     },
40143     
40144     markValid : function()
40145     {
40146         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40147             this.indicatorEl().removeClass('visible');
40148             this.indicatorEl().addClass('invisible');
40149         }
40150         
40151         
40152         if (Roo.bootstrap.version == 3) {
40153             this.el.removeClass([this.invalidClass, this.validClass]);
40154             this.el.addClass(this.validClass);
40155         } else {
40156             this.el.removeClass(['is-invalid','is-valid']);
40157             this.el.addClass(['is-valid']);
40158         }
40159         this.fireEvent('valid', this);
40160     },
40161     
40162     markInvalid : function(msg)
40163     {
40164         if(this.allowBlank || this.disabled){
40165             return;
40166         }
40167         
40168         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40169             this.indicatorEl().removeClass('invisible');
40170             this.indicatorEl().addClass('visible');
40171         }
40172         if (Roo.bootstrap.version == 3) {
40173             this.el.removeClass([this.invalidClass, this.validClass]);
40174             this.el.addClass(this.invalidClass);
40175         } else {
40176             this.el.removeClass(['is-invalid','is-valid']);
40177             this.el.addClass(['is-invalid']);
40178         }
40179         
40180         this.fireEvent('invalid', this, msg);
40181         
40182     },
40183     
40184     setValue : function(v, suppressEvent)
40185     {   
40186         if(this.value === v){
40187             return;
40188         }
40189         
40190         this.value = v;
40191         
40192         if(this.rendered){
40193             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40194         }
40195         
40196         Roo.each(this.radioes, function(i){
40197             i.checked = false;
40198             i.el.removeClass('checked');
40199         });
40200         
40201         Roo.each(this.radioes, function(i){
40202             
40203             if(i.value === v || i.value.toString() === v.toString()){
40204                 i.checked = true;
40205                 i.el.addClass('checked');
40206                 
40207                 if(suppressEvent !== true){
40208                     this.fireEvent('check', this, i);
40209                 }
40210                 
40211                 return false;
40212             }
40213             
40214         }, this);
40215         
40216         this.validate();
40217     },
40218     
40219     clearInvalid : function(){
40220         
40221         if(!this.el || this.preventMark){
40222             return;
40223         }
40224         
40225         this.el.removeClass([this.invalidClass]);
40226         
40227         this.fireEvent('valid', this);
40228     }
40229     
40230 });
40231
40232 Roo.apply(Roo.bootstrap.form.RadioSet, {
40233     
40234     groups: {},
40235     
40236     register : function(set)
40237     {
40238         this.groups[set.name] = set;
40239     },
40240     
40241     get: function(name) 
40242     {
40243         if (typeof(this.groups[name]) == 'undefined') {
40244             return false;
40245         }
40246         
40247         return this.groups[name] ;
40248     }
40249     
40250 });
40251 /*
40252  * Based on:
40253  * Ext JS Library 1.1.1
40254  * Copyright(c) 2006-2007, Ext JS, LLC.
40255  *
40256  * Originally Released Under LGPL - original licence link has changed is not relivant.
40257  *
40258  * Fork - LGPL
40259  * <script type="text/javascript">
40260  */
40261
40262
40263 /**
40264  * @class Roo.bootstrap.SplitBar
40265  * @extends Roo.util.Observable
40266  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40267  * <br><br>
40268  * Usage:
40269  * <pre><code>
40270 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40271                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40272 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40273 split.minSize = 100;
40274 split.maxSize = 600;
40275 split.animate = true;
40276 split.on('moved', splitterMoved);
40277 </code></pre>
40278  * @constructor
40279  * Create a new SplitBar
40280  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40281  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40282  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40283  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40284                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40285                         position of the SplitBar).
40286  */
40287 Roo.bootstrap.SplitBar = function(cfg){
40288     
40289     /** @private */
40290     
40291     //{
40292     //  dragElement : elm
40293     //  resizingElement: el,
40294         // optional..
40295     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40296     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40297         // existingProxy ???
40298     //}
40299     
40300     this.el = Roo.get(cfg.dragElement, true);
40301     this.el.dom.unselectable = "on";
40302     /** @private */
40303     this.resizingEl = Roo.get(cfg.resizingElement, true);
40304
40305     /**
40306      * @private
40307      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40308      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40309      * @type Number
40310      */
40311     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40312     
40313     /**
40314      * The minimum size of the resizing element. (Defaults to 0)
40315      * @type Number
40316      */
40317     this.minSize = 0;
40318     
40319     /**
40320      * The maximum size of the resizing element. (Defaults to 2000)
40321      * @type Number
40322      */
40323     this.maxSize = 2000;
40324     
40325     /**
40326      * Whether to animate the transition to the new size
40327      * @type Boolean
40328      */
40329     this.animate = false;
40330     
40331     /**
40332      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40333      * @type Boolean
40334      */
40335     this.useShim = false;
40336     
40337     /** @private */
40338     this.shim = null;
40339     
40340     if(!cfg.existingProxy){
40341         /** @private */
40342         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40343     }else{
40344         this.proxy = Roo.get(cfg.existingProxy).dom;
40345     }
40346     /** @private */
40347     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40348     
40349     /** @private */
40350     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40351     
40352     /** @private */
40353     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40354     
40355     /** @private */
40356     this.dragSpecs = {};
40357     
40358     /**
40359      * @private The adapter to use to positon and resize elements
40360      */
40361     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40362     this.adapter.init(this);
40363     
40364     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40365         /** @private */
40366         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40367         this.el.addClass("roo-splitbar-h");
40368     }else{
40369         /** @private */
40370         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40371         this.el.addClass("roo-splitbar-v");
40372     }
40373     
40374     this.addEvents({
40375         /**
40376          * @event resize
40377          * Fires when the splitter is moved (alias for {@link #event-moved})
40378          * @param {Roo.bootstrap.SplitBar} this
40379          * @param {Number} newSize the new width or height
40380          */
40381         "resize" : true,
40382         /**
40383          * @event moved
40384          * Fires when the splitter is moved
40385          * @param {Roo.bootstrap.SplitBar} this
40386          * @param {Number} newSize the new width or height
40387          */
40388         "moved" : true,
40389         /**
40390          * @event beforeresize
40391          * Fires before the splitter is dragged
40392          * @param {Roo.bootstrap.SplitBar} this
40393          */
40394         "beforeresize" : true,
40395
40396         "beforeapply" : true
40397     });
40398
40399     Roo.util.Observable.call(this);
40400 };
40401
40402 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40403     onStartProxyDrag : function(x, y){
40404         this.fireEvent("beforeresize", this);
40405         if(!this.overlay){
40406             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40407             o.unselectable();
40408             o.enableDisplayMode("block");
40409             // all splitbars share the same overlay
40410             Roo.bootstrap.SplitBar.prototype.overlay = o;
40411         }
40412         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40413         this.overlay.show();
40414         Roo.get(this.proxy).setDisplayed("block");
40415         var size = this.adapter.getElementSize(this);
40416         this.activeMinSize = this.getMinimumSize();;
40417         this.activeMaxSize = this.getMaximumSize();;
40418         var c1 = size - this.activeMinSize;
40419         var c2 = Math.max(this.activeMaxSize - size, 0);
40420         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40421             this.dd.resetConstraints();
40422             this.dd.setXConstraint(
40423                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40424                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40425             );
40426             this.dd.setYConstraint(0, 0);
40427         }else{
40428             this.dd.resetConstraints();
40429             this.dd.setXConstraint(0, 0);
40430             this.dd.setYConstraint(
40431                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40432                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40433             );
40434          }
40435         this.dragSpecs.startSize = size;
40436         this.dragSpecs.startPoint = [x, y];
40437         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40438     },
40439     
40440     /** 
40441      * @private Called after the drag operation by the DDProxy
40442      */
40443     onEndProxyDrag : function(e){
40444         Roo.get(this.proxy).setDisplayed(false);
40445         var endPoint = Roo.lib.Event.getXY(e);
40446         if(this.overlay){
40447             this.overlay.hide();
40448         }
40449         var newSize;
40450         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40451             newSize = this.dragSpecs.startSize + 
40452                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40453                     endPoint[0] - this.dragSpecs.startPoint[0] :
40454                     this.dragSpecs.startPoint[0] - endPoint[0]
40455                 );
40456         }else{
40457             newSize = this.dragSpecs.startSize + 
40458                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40459                     endPoint[1] - this.dragSpecs.startPoint[1] :
40460                     this.dragSpecs.startPoint[1] - endPoint[1]
40461                 );
40462         }
40463         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40464         if(newSize != this.dragSpecs.startSize){
40465             if(this.fireEvent('beforeapply', this, newSize) !== false){
40466                 this.adapter.setElementSize(this, newSize);
40467                 this.fireEvent("moved", this, newSize);
40468                 this.fireEvent("resize", this, newSize);
40469             }
40470         }
40471     },
40472     
40473     /**
40474      * Get the adapter this SplitBar uses
40475      * @return The adapter object
40476      */
40477     getAdapter : function(){
40478         return this.adapter;
40479     },
40480     
40481     /**
40482      * Set the adapter this SplitBar uses
40483      * @param {Object} adapter A SplitBar adapter object
40484      */
40485     setAdapter : function(adapter){
40486         this.adapter = adapter;
40487         this.adapter.init(this);
40488     },
40489     
40490     /**
40491      * Gets the minimum size for the resizing element
40492      * @return {Number} The minimum size
40493      */
40494     getMinimumSize : function(){
40495         return this.minSize;
40496     },
40497     
40498     /**
40499      * Sets the minimum size for the resizing element
40500      * @param {Number} minSize The minimum size
40501      */
40502     setMinimumSize : function(minSize){
40503         this.minSize = minSize;
40504     },
40505     
40506     /**
40507      * Gets the maximum size for the resizing element
40508      * @return {Number} The maximum size
40509      */
40510     getMaximumSize : function(){
40511         return this.maxSize;
40512     },
40513     
40514     /**
40515      * Sets the maximum size for the resizing element
40516      * @param {Number} maxSize The maximum size
40517      */
40518     setMaximumSize : function(maxSize){
40519         this.maxSize = maxSize;
40520     },
40521     
40522     /**
40523      * Sets the initialize size for the resizing element
40524      * @param {Number} size The initial size
40525      */
40526     setCurrentSize : function(size){
40527         var oldAnimate = this.animate;
40528         this.animate = false;
40529         this.adapter.setElementSize(this, size);
40530         this.animate = oldAnimate;
40531     },
40532     
40533     /**
40534      * Destroy this splitbar. 
40535      * @param {Boolean} removeEl True to remove the element
40536      */
40537     destroy : function(removeEl){
40538         if(this.shim){
40539             this.shim.remove();
40540         }
40541         this.dd.unreg();
40542         this.proxy.parentNode.removeChild(this.proxy);
40543         if(removeEl){
40544             this.el.remove();
40545         }
40546     }
40547 });
40548
40549 /**
40550  * @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.
40551  */
40552 Roo.bootstrap.SplitBar.createProxy = function(dir){
40553     var proxy = new Roo.Element(document.createElement("div"));
40554     proxy.unselectable();
40555     var cls = 'roo-splitbar-proxy';
40556     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40557     document.body.appendChild(proxy.dom);
40558     return proxy.dom;
40559 };
40560
40561 /** 
40562  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40563  * Default Adapter. It assumes the splitter and resizing element are not positioned
40564  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40565  */
40566 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40567 };
40568
40569 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40570     // do nothing for now
40571     init : function(s){
40572     
40573     },
40574     /**
40575      * Called before drag operations to get the current size of the resizing element. 
40576      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40577      */
40578      getElementSize : function(s){
40579         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40580             return s.resizingEl.getWidth();
40581         }else{
40582             return s.resizingEl.getHeight();
40583         }
40584     },
40585     
40586     /**
40587      * Called after drag operations to set the size of the resizing element.
40588      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40589      * @param {Number} newSize The new size to set
40590      * @param {Function} onComplete A function to be invoked when resizing is complete
40591      */
40592     setElementSize : function(s, newSize, onComplete){
40593         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40594             if(!s.animate){
40595                 s.resizingEl.setWidth(newSize);
40596                 if(onComplete){
40597                     onComplete(s, newSize);
40598                 }
40599             }else{
40600                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40601             }
40602         }else{
40603             
40604             if(!s.animate){
40605                 s.resizingEl.setHeight(newSize);
40606                 if(onComplete){
40607                     onComplete(s, newSize);
40608                 }
40609             }else{
40610                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40611             }
40612         }
40613     }
40614 };
40615
40616 /** 
40617  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40618  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40619  * Adapter that  moves the splitter element to align with the resized sizing element. 
40620  * Used with an absolute positioned SplitBar.
40621  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40622  * document.body, make sure you assign an id to the body element.
40623  */
40624 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40625     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40626     this.container = Roo.get(container);
40627 };
40628
40629 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40630     init : function(s){
40631         this.basic.init(s);
40632     },
40633     
40634     getElementSize : function(s){
40635         return this.basic.getElementSize(s);
40636     },
40637     
40638     setElementSize : function(s, newSize, onComplete){
40639         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40640     },
40641     
40642     moveSplitter : function(s){
40643         var yes = Roo.bootstrap.SplitBar;
40644         switch(s.placement){
40645             case yes.LEFT:
40646                 s.el.setX(s.resizingEl.getRight());
40647                 break;
40648             case yes.RIGHT:
40649                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40650                 break;
40651             case yes.TOP:
40652                 s.el.setY(s.resizingEl.getBottom());
40653                 break;
40654             case yes.BOTTOM:
40655                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40656                 break;
40657         }
40658     }
40659 };
40660
40661 /**
40662  * Orientation constant - Create a vertical SplitBar
40663  * @static
40664  * @type Number
40665  */
40666 Roo.bootstrap.SplitBar.VERTICAL = 1;
40667
40668 /**
40669  * Orientation constant - Create a horizontal SplitBar
40670  * @static
40671  * @type Number
40672  */
40673 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40674
40675 /**
40676  * Placement constant - The resizing element is to the left of the splitter element
40677  * @static
40678  * @type Number
40679  */
40680 Roo.bootstrap.SplitBar.LEFT = 1;
40681
40682 /**
40683  * Placement constant - The resizing element is to the right of the splitter element
40684  * @static
40685  * @type Number
40686  */
40687 Roo.bootstrap.SplitBar.RIGHT = 2;
40688
40689 /**
40690  * Placement constant - The resizing element is positioned above the splitter element
40691  * @static
40692  * @type Number
40693  */
40694 Roo.bootstrap.SplitBar.TOP = 3;
40695
40696 /**
40697  * Placement constant - The resizing element is positioned under splitter element
40698  * @static
40699  * @type Number
40700  */
40701 Roo.bootstrap.SplitBar.BOTTOM = 4;
40702 /*
40703  * Based on:
40704  * Ext JS Library 1.1.1
40705  * Copyright(c) 2006-2007, Ext JS, LLC.
40706  *
40707  * Originally Released Under LGPL - original licence link has changed is not relivant.
40708  *
40709  * Fork - LGPL
40710  * <script type="text/javascript">
40711  */
40712
40713 /**
40714  * @class Roo.bootstrap.layout.Manager
40715  * @extends Roo.bootstrap.Component
40716  * @abstract
40717  * Base class for layout managers.
40718  */
40719 Roo.bootstrap.layout.Manager = function(config)
40720 {
40721     this.monitorWindowResize = true; // do this before we apply configuration.
40722     
40723     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40724
40725
40726
40727
40728
40729     /** false to disable window resize monitoring @type Boolean */
40730     
40731     this.regions = {};
40732     this.addEvents({
40733         /**
40734          * @event layout
40735          * Fires when a layout is performed.
40736          * @param {Roo.LayoutManager} this
40737          */
40738         "layout" : true,
40739         /**
40740          * @event regionresized
40741          * Fires when the user resizes a region.
40742          * @param {Roo.LayoutRegion} region The resized region
40743          * @param {Number} newSize The new size (width for east/west, height for north/south)
40744          */
40745         "regionresized" : true,
40746         /**
40747          * @event regioncollapsed
40748          * Fires when a region is collapsed.
40749          * @param {Roo.LayoutRegion} region The collapsed region
40750          */
40751         "regioncollapsed" : true,
40752         /**
40753          * @event regionexpanded
40754          * Fires when a region is expanded.
40755          * @param {Roo.LayoutRegion} region The expanded region
40756          */
40757         "regionexpanded" : true
40758     });
40759     this.updating = false;
40760
40761     if (config.el) {
40762         this.el = Roo.get(config.el);
40763         this.initEvents();
40764     }
40765
40766 };
40767
40768 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40769
40770
40771     regions : null,
40772
40773     monitorWindowResize : true,
40774
40775
40776     updating : false,
40777
40778
40779     onRender : function(ct, position)
40780     {
40781         if(!this.el){
40782             this.el = Roo.get(ct);
40783             this.initEvents();
40784         }
40785         //this.fireEvent('render',this);
40786     },
40787
40788
40789     initEvents: function()
40790     {
40791
40792
40793         // ie scrollbar fix
40794         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40795             document.body.scroll = "no";
40796         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40797             this.el.position('relative');
40798         }
40799         this.id = this.el.id;
40800         this.el.addClass("roo-layout-container");
40801         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40802         if(this.el.dom != document.body ) {
40803             this.el.on('resize', this.layout,this);
40804             this.el.on('show', this.layout,this);
40805         }
40806
40807     },
40808
40809     /**
40810      * Returns true if this layout is currently being updated
40811      * @return {Boolean}
40812      */
40813     isUpdating : function(){
40814         return this.updating;
40815     },
40816
40817     /**
40818      * Suspend the LayoutManager from doing auto-layouts while
40819      * making multiple add or remove calls
40820      */
40821     beginUpdate : function(){
40822         this.updating = true;
40823     },
40824
40825     /**
40826      * Restore auto-layouts and optionally disable the manager from performing a layout
40827      * @param {Boolean} noLayout true to disable a layout update
40828      */
40829     endUpdate : function(noLayout){
40830         this.updating = false;
40831         if(!noLayout){
40832             this.layout();
40833         }
40834     },
40835
40836     layout: function(){
40837         // abstract...
40838     },
40839
40840     onRegionResized : function(region, newSize){
40841         this.fireEvent("regionresized", region, newSize);
40842         this.layout();
40843     },
40844
40845     onRegionCollapsed : function(region){
40846         this.fireEvent("regioncollapsed", region);
40847     },
40848
40849     onRegionExpanded : function(region){
40850         this.fireEvent("regionexpanded", region);
40851     },
40852
40853     /**
40854      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40855      * performs box-model adjustments.
40856      * @return {Object} The size as an object {width: (the width), height: (the height)}
40857      */
40858     getViewSize : function()
40859     {
40860         var size;
40861         if(this.el.dom != document.body){
40862             size = this.el.getSize();
40863         }else{
40864             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40865         }
40866         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40867         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40868         return size;
40869     },
40870
40871     /**
40872      * Returns the Element this layout is bound to.
40873      * @return {Roo.Element}
40874      */
40875     getEl : function(){
40876         return this.el;
40877     },
40878
40879     /**
40880      * Returns the specified region.
40881      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40882      * @return {Roo.LayoutRegion}
40883      */
40884     getRegion : function(target){
40885         return this.regions[target.toLowerCase()];
40886     },
40887
40888     onWindowResize : function(){
40889         if(this.monitorWindowResize){
40890             this.layout();
40891         }
40892     }
40893 });
40894 /*
40895  * Based on:
40896  * Ext JS Library 1.1.1
40897  * Copyright(c) 2006-2007, Ext JS, LLC.
40898  *
40899  * Originally Released Under LGPL - original licence link has changed is not relivant.
40900  *
40901  * Fork - LGPL
40902  * <script type="text/javascript">
40903  */
40904 /**
40905  * @class Roo.bootstrap.layout.Border
40906  * @extends Roo.bootstrap.layout.Manager
40907  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40908  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40909  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40910  * please see: examples/bootstrap/nested.html<br><br>
40911  
40912 <b>The container the layout is rendered into can be either the body element or any other element.
40913 If it is not the body element, the container needs to either be an absolute positioned element,
40914 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40915 the container size if it is not the body element.</b>
40916
40917 * @constructor
40918 * Create a new Border
40919 * @param {Object} config Configuration options
40920  */
40921 Roo.bootstrap.layout.Border = function(config){
40922     config = config || {};
40923     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40924     
40925     
40926     
40927     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40928         if(config[region]){
40929             config[region].region = region;
40930             this.addRegion(config[region]);
40931         }
40932     },this);
40933     
40934 };
40935
40936 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40937
40938 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40939     
40940         /**
40941          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40942          */
40943         /**
40944          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40945          */
40946         /**
40947          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40948          */
40949         /**
40950          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40951          */
40952         /**
40953          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40954          */
40955         
40956         
40957         
40958         
40959     parent : false, // this might point to a 'nest' or a ???
40960     
40961     /**
40962      * Creates and adds a new region if it doesn't already exist.
40963      * @param {String} target The target region key (north, south, east, west or center).
40964      * @param {Object} config The regions config object
40965      * @return {BorderLayoutRegion} The new region
40966      */
40967     addRegion : function(config)
40968     {
40969         if(!this.regions[config.region]){
40970             var r = this.factory(config);
40971             this.bindRegion(r);
40972         }
40973         return this.regions[config.region];
40974     },
40975
40976     // private (kinda)
40977     bindRegion : function(r){
40978         this.regions[r.config.region] = r;
40979         
40980         r.on("visibilitychange",    this.layout, this);
40981         r.on("paneladded",          this.layout, this);
40982         r.on("panelremoved",        this.layout, this);
40983         r.on("invalidated",         this.layout, this);
40984         r.on("resized",             this.onRegionResized, this);
40985         r.on("collapsed",           this.onRegionCollapsed, this);
40986         r.on("expanded",            this.onRegionExpanded, this);
40987     },
40988
40989     /**
40990      * Performs a layout update.
40991      */
40992     layout : function()
40993     {
40994         if(this.updating) {
40995             return;
40996         }
40997         
40998         // render all the rebions if they have not been done alreayd?
40999         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41000             if(this.regions[region] && !this.regions[region].bodyEl){
41001                 this.regions[region].onRender(this.el)
41002             }
41003         },this);
41004         
41005         var size = this.getViewSize();
41006         var w = size.width;
41007         var h = size.height;
41008         var centerW = w;
41009         var centerH = h;
41010         var centerY = 0;
41011         var centerX = 0;
41012         //var x = 0, y = 0;
41013
41014         var rs = this.regions;
41015         var north = rs["north"];
41016         var south = rs["south"]; 
41017         var west = rs["west"];
41018         var east = rs["east"];
41019         var center = rs["center"];
41020         //if(this.hideOnLayout){ // not supported anymore
41021             //c.el.setStyle("display", "none");
41022         //}
41023         if(north && north.isVisible()){
41024             var b = north.getBox();
41025             var m = north.getMargins();
41026             b.width = w - (m.left+m.right);
41027             b.x = m.left;
41028             b.y = m.top;
41029             centerY = b.height + b.y + m.bottom;
41030             centerH -= centerY;
41031             north.updateBox(this.safeBox(b));
41032         }
41033         if(south && south.isVisible()){
41034             var b = south.getBox();
41035             var m = south.getMargins();
41036             b.width = w - (m.left+m.right);
41037             b.x = m.left;
41038             var totalHeight = (b.height + m.top + m.bottom);
41039             b.y = h - totalHeight + m.top;
41040             centerH -= totalHeight;
41041             south.updateBox(this.safeBox(b));
41042         }
41043         if(west && west.isVisible()){
41044             var b = west.getBox();
41045             var m = west.getMargins();
41046             b.height = centerH - (m.top+m.bottom);
41047             b.x = m.left;
41048             b.y = centerY + m.top;
41049             var totalWidth = (b.width + m.left + m.right);
41050             centerX += totalWidth;
41051             centerW -= totalWidth;
41052             west.updateBox(this.safeBox(b));
41053         }
41054         if(east && east.isVisible()){
41055             var b = east.getBox();
41056             var m = east.getMargins();
41057             b.height = centerH - (m.top+m.bottom);
41058             var totalWidth = (b.width + m.left + m.right);
41059             b.x = w - totalWidth + m.left;
41060             b.y = centerY + m.top;
41061             centerW -= totalWidth;
41062             east.updateBox(this.safeBox(b));
41063         }
41064         if(center){
41065             var m = center.getMargins();
41066             var centerBox = {
41067                 x: centerX + m.left,
41068                 y: centerY + m.top,
41069                 width: centerW - (m.left+m.right),
41070                 height: centerH - (m.top+m.bottom)
41071             };
41072             //if(this.hideOnLayout){
41073                 //center.el.setStyle("display", "block");
41074             //}
41075             center.updateBox(this.safeBox(centerBox));
41076         }
41077         this.el.repaint();
41078         this.fireEvent("layout", this);
41079     },
41080
41081     // private
41082     safeBox : function(box){
41083         box.width = Math.max(0, box.width);
41084         box.height = Math.max(0, box.height);
41085         return box;
41086     },
41087
41088     /**
41089      * Adds a ContentPanel (or subclass) to this layout.
41090      * @param {String} target The target region key (north, south, east, west or center).
41091      * @param {Roo.ContentPanel} panel The panel to add
41092      * @return {Roo.ContentPanel} The added panel
41093      */
41094     add : function(target, panel){
41095          
41096         target = target.toLowerCase();
41097         return this.regions[target].add(panel);
41098     },
41099
41100     /**
41101      * Remove a ContentPanel (or subclass) to this layout.
41102      * @param {String} target The target region key (north, south, east, west or center).
41103      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41104      * @return {Roo.ContentPanel} The removed panel
41105      */
41106     remove : function(target, panel){
41107         target = target.toLowerCase();
41108         return this.regions[target].remove(panel);
41109     },
41110
41111     /**
41112      * Searches all regions for a panel with the specified id
41113      * @param {String} panelId
41114      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41115      */
41116     findPanel : function(panelId){
41117         var rs = this.regions;
41118         for(var target in rs){
41119             if(typeof rs[target] != "function"){
41120                 var p = rs[target].getPanel(panelId);
41121                 if(p){
41122                     return p;
41123                 }
41124             }
41125         }
41126         return null;
41127     },
41128
41129     /**
41130      * Searches all regions for a panel with the specified id and activates (shows) it.
41131      * @param {String/ContentPanel} panelId The panels id or the panel itself
41132      * @return {Roo.ContentPanel} The shown panel or null
41133      */
41134     showPanel : function(panelId) {
41135       var rs = this.regions;
41136       for(var target in rs){
41137          var r = rs[target];
41138          if(typeof r != "function"){
41139             if(r.hasPanel(panelId)){
41140                return r.showPanel(panelId);
41141             }
41142          }
41143       }
41144       return null;
41145    },
41146
41147    /**
41148      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41149      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41150      */
41151    /*
41152     restoreState : function(provider){
41153         if(!provider){
41154             provider = Roo.state.Manager;
41155         }
41156         var sm = new Roo.LayoutStateManager();
41157         sm.init(this, provider);
41158     },
41159 */
41160  
41161  
41162     /**
41163      * Adds a xtype elements to the layout.
41164      * <pre><code>
41165
41166 layout.addxtype({
41167        xtype : 'ContentPanel',
41168        region: 'west',
41169        items: [ .... ]
41170    }
41171 );
41172
41173 layout.addxtype({
41174         xtype : 'NestedLayoutPanel',
41175         region: 'west',
41176         layout: {
41177            center: { },
41178            west: { }   
41179         },
41180         items : [ ... list of content panels or nested layout panels.. ]
41181    }
41182 );
41183 </code></pre>
41184      * @param {Object} cfg Xtype definition of item to add.
41185      */
41186     addxtype : function(cfg)
41187     {
41188         // basically accepts a pannel...
41189         // can accept a layout region..!?!?
41190         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41191         
41192         
41193         // theory?  children can only be panels??
41194         
41195         //if (!cfg.xtype.match(/Panel$/)) {
41196         //    return false;
41197         //}
41198         var ret = false;
41199         
41200         if (typeof(cfg.region) == 'undefined') {
41201             Roo.log("Failed to add Panel, region was not set");
41202             Roo.log(cfg);
41203             return false;
41204         }
41205         var region = cfg.region;
41206         delete cfg.region;
41207         
41208           
41209         var xitems = [];
41210         if (cfg.items) {
41211             xitems = cfg.items;
41212             delete cfg.items;
41213         }
41214         var nb = false;
41215         
41216         if ( region == 'center') {
41217             Roo.log("Center: " + cfg.title);
41218         }
41219         
41220         
41221         switch(cfg.xtype) 
41222         {
41223             case 'Content':  // ContentPanel (el, cfg)
41224             case 'Scroll':  // ContentPanel (el, cfg)
41225             case 'View': 
41226                 cfg.autoCreate = cfg.autoCreate || true;
41227                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41228                 //} else {
41229                 //    var el = this.el.createChild();
41230                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41231                 //}
41232                 
41233                 this.add(region, ret);
41234                 break;
41235             
41236             /*
41237             case 'TreePanel': // our new panel!
41238                 cfg.el = this.el.createChild();
41239                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41240                 this.add(region, ret);
41241                 break;
41242             */
41243             
41244             case 'Nest': 
41245                 // create a new Layout (which is  a Border Layout...
41246                 
41247                 var clayout = cfg.layout;
41248                 clayout.el  = this.el.createChild();
41249                 clayout.items   = clayout.items  || [];
41250                 
41251                 delete cfg.layout;
41252                 
41253                 // replace this exitems with the clayout ones..
41254                 xitems = clayout.items;
41255                  
41256                 // force background off if it's in center...
41257                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41258                     cfg.background = false;
41259                 }
41260                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41261                 
41262                 
41263                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41264                 //console.log('adding nested layout panel '  + cfg.toSource());
41265                 this.add(region, ret);
41266                 nb = {}; /// find first...
41267                 break;
41268             
41269             case 'Grid':
41270                 
41271                 // needs grid and region
41272                 
41273                 //var el = this.getRegion(region).el.createChild();
41274                 /*
41275                  *var el = this.el.createChild();
41276                 // create the grid first...
41277                 cfg.grid.container = el;
41278                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41279                 */
41280                 
41281                 if (region == 'center' && this.active ) {
41282                     cfg.background = false;
41283                 }
41284                 
41285                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41286                 
41287                 this.add(region, ret);
41288                 /*
41289                 if (cfg.background) {
41290                     // render grid on panel activation (if panel background)
41291                     ret.on('activate', function(gp) {
41292                         if (!gp.grid.rendered) {
41293                     //        gp.grid.render(el);
41294                         }
41295                     });
41296                 } else {
41297                   //  cfg.grid.render(el);
41298                 }
41299                 */
41300                 break;
41301            
41302            
41303             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41304                 // it was the old xcomponent building that caused this before.
41305                 // espeically if border is the top element in the tree.
41306                 ret = this;
41307                 break; 
41308                 
41309                     
41310                 
41311                 
41312                 
41313             default:
41314                 /*
41315                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41316                     
41317                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41318                     this.add(region, ret);
41319                 } else {
41320                 */
41321                     Roo.log(cfg);
41322                     throw "Can not add '" + cfg.xtype + "' to Border";
41323                     return null;
41324              
41325                                 
41326              
41327         }
41328         this.beginUpdate();
41329         // add children..
41330         var region = '';
41331         var abn = {};
41332         Roo.each(xitems, function(i)  {
41333             region = nb && i.region ? i.region : false;
41334             
41335             var add = ret.addxtype(i);
41336            
41337             if (region) {
41338                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41339                 if (!i.background) {
41340                     abn[region] = nb[region] ;
41341                 }
41342             }
41343             
41344         });
41345         this.endUpdate();
41346
41347         // make the last non-background panel active..
41348         //if (nb) { Roo.log(abn); }
41349         if (nb) {
41350             
41351             for(var r in abn) {
41352                 region = this.getRegion(r);
41353                 if (region) {
41354                     // tried using nb[r], but it does not work..
41355                      
41356                     region.showPanel(abn[r]);
41357                    
41358                 }
41359             }
41360         }
41361         return ret;
41362         
41363     },
41364     
41365     
41366 // private
41367     factory : function(cfg)
41368     {
41369         
41370         var validRegions = Roo.bootstrap.layout.Border.regions;
41371
41372         var target = cfg.region;
41373         cfg.mgr = this;
41374         
41375         var r = Roo.bootstrap.layout;
41376         Roo.log(target);
41377         switch(target){
41378             case "north":
41379                 return new r.North(cfg);
41380             case "south":
41381                 return new r.South(cfg);
41382             case "east":
41383                 return new r.East(cfg);
41384             case "west":
41385                 return new r.West(cfg);
41386             case "center":
41387                 return new r.Center(cfg);
41388         }
41389         throw 'Layout region "'+target+'" not supported.';
41390     }
41391     
41392     
41393 });
41394  /*
41395  * Based on:
41396  * Ext JS Library 1.1.1
41397  * Copyright(c) 2006-2007, Ext JS, LLC.
41398  *
41399  * Originally Released Under LGPL - original licence link has changed is not relivant.
41400  *
41401  * Fork - LGPL
41402  * <script type="text/javascript">
41403  */
41404  
41405 /**
41406  * @class Roo.bootstrap.layout.Basic
41407  * @extends Roo.util.Observable
41408  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41409  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41410  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41411  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41412  * @cfg {string}   region  the region that it inhabits..
41413  * @cfg {bool}   skipConfig skip config?
41414  * 
41415
41416  */
41417 Roo.bootstrap.layout.Basic = function(config){
41418     
41419     this.mgr = config.mgr;
41420     
41421     this.position = config.region;
41422     
41423     var skipConfig = config.skipConfig;
41424     
41425     this.events = {
41426         /**
41427          * @scope Roo.BasicLayoutRegion
41428          */
41429         
41430         /**
41431          * @event beforeremove
41432          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41433          * @param {Roo.LayoutRegion} this
41434          * @param {Roo.ContentPanel} panel The panel
41435          * @param {Object} e The cancel event object
41436          */
41437         "beforeremove" : true,
41438         /**
41439          * @event invalidated
41440          * Fires when the layout for this region is changed.
41441          * @param {Roo.LayoutRegion} this
41442          */
41443         "invalidated" : true,
41444         /**
41445          * @event visibilitychange
41446          * Fires when this region is shown or hidden 
41447          * @param {Roo.LayoutRegion} this
41448          * @param {Boolean} visibility true or false
41449          */
41450         "visibilitychange" : true,
41451         /**
41452          * @event paneladded
41453          * Fires when a panel is added. 
41454          * @param {Roo.LayoutRegion} this
41455          * @param {Roo.ContentPanel} panel The panel
41456          */
41457         "paneladded" : true,
41458         /**
41459          * @event panelremoved
41460          * Fires when a panel is removed. 
41461          * @param {Roo.LayoutRegion} this
41462          * @param {Roo.ContentPanel} panel The panel
41463          */
41464         "panelremoved" : true,
41465         /**
41466          * @event beforecollapse
41467          * Fires when this region before collapse.
41468          * @param {Roo.LayoutRegion} this
41469          */
41470         "beforecollapse" : true,
41471         /**
41472          * @event collapsed
41473          * Fires when this region is collapsed.
41474          * @param {Roo.LayoutRegion} this
41475          */
41476         "collapsed" : true,
41477         /**
41478          * @event expanded
41479          * Fires when this region is expanded.
41480          * @param {Roo.LayoutRegion} this
41481          */
41482         "expanded" : true,
41483         /**
41484          * @event slideshow
41485          * Fires when this region is slid into view.
41486          * @param {Roo.LayoutRegion} this
41487          */
41488         "slideshow" : true,
41489         /**
41490          * @event slidehide
41491          * Fires when this region slides out of view. 
41492          * @param {Roo.LayoutRegion} this
41493          */
41494         "slidehide" : true,
41495         /**
41496          * @event panelactivated
41497          * Fires when a panel is activated. 
41498          * @param {Roo.LayoutRegion} this
41499          * @param {Roo.ContentPanel} panel The activated panel
41500          */
41501         "panelactivated" : true,
41502         /**
41503          * @event resized
41504          * Fires when the user resizes this region. 
41505          * @param {Roo.LayoutRegion} this
41506          * @param {Number} newSize The new size (width for east/west, height for north/south)
41507          */
41508         "resized" : true
41509     };
41510     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41511     this.panels = new Roo.util.MixedCollection();
41512     this.panels.getKey = this.getPanelId.createDelegate(this);
41513     this.box = null;
41514     this.activePanel = null;
41515     // ensure listeners are added...
41516     
41517     if (config.listeners || config.events) {
41518         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41519             listeners : config.listeners || {},
41520             events : config.events || {}
41521         });
41522     }
41523     
41524     if(skipConfig !== true){
41525         this.applyConfig(config);
41526     }
41527 };
41528
41529 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41530 {
41531     getPanelId : function(p){
41532         return p.getId();
41533     },
41534     
41535     applyConfig : function(config){
41536         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41537         this.config = config;
41538         
41539     },
41540     
41541     /**
41542      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41543      * the width, for horizontal (north, south) the height.
41544      * @param {Number} newSize The new width or height
41545      */
41546     resizeTo : function(newSize){
41547         var el = this.el ? this.el :
41548                  (this.activePanel ? this.activePanel.getEl() : null);
41549         if(el){
41550             switch(this.position){
41551                 case "east":
41552                 case "west":
41553                     el.setWidth(newSize);
41554                     this.fireEvent("resized", this, newSize);
41555                 break;
41556                 case "north":
41557                 case "south":
41558                     el.setHeight(newSize);
41559                     this.fireEvent("resized", this, newSize);
41560                 break;                
41561             }
41562         }
41563     },
41564     
41565     getBox : function(){
41566         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41567     },
41568     
41569     getMargins : function(){
41570         return this.margins;
41571     },
41572     
41573     updateBox : function(box){
41574         this.box = box;
41575         var el = this.activePanel.getEl();
41576         el.dom.style.left = box.x + "px";
41577         el.dom.style.top = box.y + "px";
41578         this.activePanel.setSize(box.width, box.height);
41579     },
41580     
41581     /**
41582      * Returns the container element for this region.
41583      * @return {Roo.Element}
41584      */
41585     getEl : function(){
41586         return this.activePanel;
41587     },
41588     
41589     /**
41590      * Returns true if this region is currently visible.
41591      * @return {Boolean}
41592      */
41593     isVisible : function(){
41594         return this.activePanel ? true : false;
41595     },
41596     
41597     setActivePanel : function(panel){
41598         panel = this.getPanel(panel);
41599         if(this.activePanel && this.activePanel != panel){
41600             this.activePanel.setActiveState(false);
41601             this.activePanel.getEl().setLeftTop(-10000,-10000);
41602         }
41603         this.activePanel = panel;
41604         panel.setActiveState(true);
41605         if(this.box){
41606             panel.setSize(this.box.width, this.box.height);
41607         }
41608         this.fireEvent("panelactivated", this, panel);
41609         this.fireEvent("invalidated");
41610     },
41611     
41612     /**
41613      * Show the specified panel.
41614      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41615      * @return {Roo.ContentPanel} The shown panel or null
41616      */
41617     showPanel : function(panel){
41618         panel = this.getPanel(panel);
41619         if(panel){
41620             this.setActivePanel(panel);
41621         }
41622         return panel;
41623     },
41624     
41625     /**
41626      * Get the active panel for this region.
41627      * @return {Roo.ContentPanel} The active panel or null
41628      */
41629     getActivePanel : function(){
41630         return this.activePanel;
41631     },
41632     
41633     /**
41634      * Add the passed ContentPanel(s)
41635      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41636      * @return {Roo.ContentPanel} The panel added (if only one was added)
41637      */
41638     add : function(panel){
41639         if(arguments.length > 1){
41640             for(var i = 0, len = arguments.length; i < len; i++) {
41641                 this.add(arguments[i]);
41642             }
41643             return null;
41644         }
41645         if(this.hasPanel(panel)){
41646             this.showPanel(panel);
41647             return panel;
41648         }
41649         var el = panel.getEl();
41650         if(el.dom.parentNode != this.mgr.el.dom){
41651             this.mgr.el.dom.appendChild(el.dom);
41652         }
41653         if(panel.setRegion){
41654             panel.setRegion(this);
41655         }
41656         this.panels.add(panel);
41657         el.setStyle("position", "absolute");
41658         if(!panel.background){
41659             this.setActivePanel(panel);
41660             if(this.config.initialSize && this.panels.getCount()==1){
41661                 this.resizeTo(this.config.initialSize);
41662             }
41663         }
41664         this.fireEvent("paneladded", this, panel);
41665         return panel;
41666     },
41667     
41668     /**
41669      * Returns true if the panel is in this region.
41670      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41671      * @return {Boolean}
41672      */
41673     hasPanel : function(panel){
41674         if(typeof panel == "object"){ // must be panel obj
41675             panel = panel.getId();
41676         }
41677         return this.getPanel(panel) ? true : false;
41678     },
41679     
41680     /**
41681      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41682      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41683      * @param {Boolean} preservePanel Overrides the config preservePanel option
41684      * @return {Roo.ContentPanel} The panel that was removed
41685      */
41686     remove : function(panel, preservePanel){
41687         panel = this.getPanel(panel);
41688         if(!panel){
41689             return null;
41690         }
41691         var e = {};
41692         this.fireEvent("beforeremove", this, panel, e);
41693         if(e.cancel === true){
41694             return null;
41695         }
41696         var panelId = panel.getId();
41697         this.panels.removeKey(panelId);
41698         return panel;
41699     },
41700     
41701     /**
41702      * Returns the panel specified or null if it's not in this region.
41703      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41704      * @return {Roo.ContentPanel}
41705      */
41706     getPanel : function(id){
41707         if(typeof id == "object"){ // must be panel obj
41708             return id;
41709         }
41710         return this.panels.get(id);
41711     },
41712     
41713     /**
41714      * Returns this regions position (north/south/east/west/center).
41715      * @return {String} 
41716      */
41717     getPosition: function(){
41718         return this.position;    
41719     }
41720 });/*
41721  * Based on:
41722  * Ext JS Library 1.1.1
41723  * Copyright(c) 2006-2007, Ext JS, LLC.
41724  *
41725  * Originally Released Under LGPL - original licence link has changed is not relivant.
41726  *
41727  * Fork - LGPL
41728  * <script type="text/javascript">
41729  */
41730  
41731 /**
41732  * @class Roo.bootstrap.layout.Region
41733  * @extends Roo.bootstrap.layout.Basic
41734  * This class represents a region in a layout manager.
41735  
41736  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41737  * @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})
41738  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41739  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41740  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41741  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41742  * @cfg {String}    title           The title for the region (overrides panel titles)
41743  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41744  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41745  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41746  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41747  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41748  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41749  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41750  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41751  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41752  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41753
41754  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41755  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41756  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41757  * @cfg {Number}    width           For East/West panels
41758  * @cfg {Number}    height          For North/South panels
41759  * @cfg {Boolean}   split           To show the splitter
41760  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41761  * 
41762  * @cfg {string}   cls             Extra CSS classes to add to region
41763  * 
41764  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41765  * @cfg {string}   region  the region that it inhabits..
41766  *
41767
41768  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41769  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41770
41771  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41772  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41773  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41774  */
41775 Roo.bootstrap.layout.Region = function(config)
41776 {
41777     this.applyConfig(config);
41778
41779     var mgr = config.mgr;
41780     var pos = config.region;
41781     config.skipConfig = true;
41782     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41783     
41784     if (mgr.el) {
41785         this.onRender(mgr.el);   
41786     }
41787      
41788     this.visible = true;
41789     this.collapsed = false;
41790     this.unrendered_panels = [];
41791 };
41792
41793 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41794
41795     position: '', // set by wrapper (eg. north/south etc..)
41796     unrendered_panels : null,  // unrendered panels.
41797     
41798     tabPosition : false,
41799     
41800     mgr: false, // points to 'Border'
41801     
41802     
41803     createBody : function(){
41804         /** This region's body element 
41805         * @type Roo.Element */
41806         this.bodyEl = this.el.createChild({
41807                 tag: "div",
41808                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41809         });
41810     },
41811
41812     onRender: function(ctr, pos)
41813     {
41814         var dh = Roo.DomHelper;
41815         /** This region's container element 
41816         * @type Roo.Element */
41817         this.el = dh.append(ctr.dom, {
41818                 tag: "div",
41819                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41820             }, true);
41821         /** This region's title element 
41822         * @type Roo.Element */
41823     
41824         this.titleEl = dh.append(this.el.dom,  {
41825                 tag: "div",
41826                 unselectable: "on",
41827                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41828                 children:[
41829                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41830                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41831                 ]
41832             }, true);
41833         
41834         this.titleEl.enableDisplayMode();
41835         /** This region's title text element 
41836         * @type HTMLElement */
41837         this.titleTextEl = this.titleEl.dom.firstChild;
41838         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41839         /*
41840         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41841         this.closeBtn.enableDisplayMode();
41842         this.closeBtn.on("click", this.closeClicked, this);
41843         this.closeBtn.hide();
41844     */
41845         this.createBody(this.config);
41846         if(this.config.hideWhenEmpty){
41847             this.hide();
41848             this.on("paneladded", this.validateVisibility, this);
41849             this.on("panelremoved", this.validateVisibility, this);
41850         }
41851         if(this.autoScroll){
41852             this.bodyEl.setStyle("overflow", "auto");
41853         }else{
41854             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41855         }
41856         //if(c.titlebar !== false){
41857             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41858                 this.titleEl.hide();
41859             }else{
41860                 this.titleEl.show();
41861                 if(this.config.title){
41862                     this.titleTextEl.innerHTML = this.config.title;
41863                 }
41864             }
41865         //}
41866         if(this.config.collapsed){
41867             this.collapse(true);
41868         }
41869         if(this.config.hidden){
41870             this.hide();
41871         }
41872         
41873         if (this.unrendered_panels && this.unrendered_panels.length) {
41874             for (var i =0;i< this.unrendered_panels.length; i++) {
41875                 this.add(this.unrendered_panels[i]);
41876             }
41877             this.unrendered_panels = null;
41878             
41879         }
41880         
41881     },
41882     
41883     applyConfig : function(c)
41884     {
41885         /*
41886          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41887             var dh = Roo.DomHelper;
41888             if(c.titlebar !== false){
41889                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41890                 this.collapseBtn.on("click", this.collapse, this);
41891                 this.collapseBtn.enableDisplayMode();
41892                 /*
41893                 if(c.showPin === true || this.showPin){
41894                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41895                     this.stickBtn.enableDisplayMode();
41896                     this.stickBtn.on("click", this.expand, this);
41897                     this.stickBtn.hide();
41898                 }
41899                 
41900             }
41901             */
41902             /** This region's collapsed element
41903             * @type Roo.Element */
41904             /*
41905              *
41906             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41907                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41908             ]}, true);
41909             
41910             if(c.floatable !== false){
41911                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41912                this.collapsedEl.on("click", this.collapseClick, this);
41913             }
41914
41915             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41916                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41917                    id: "message", unselectable: "on", style:{"float":"left"}});
41918                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41919              }
41920             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41921             this.expandBtn.on("click", this.expand, this);
41922             
41923         }
41924         
41925         if(this.collapseBtn){
41926             this.collapseBtn.setVisible(c.collapsible == true);
41927         }
41928         
41929         this.cmargins = c.cmargins || this.cmargins ||
41930                          (this.position == "west" || this.position == "east" ?
41931                              {top: 0, left: 2, right:2, bottom: 0} :
41932                              {top: 2, left: 0, right:0, bottom: 2});
41933         */
41934         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41935         
41936         
41937         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41938         
41939         this.autoScroll = c.autoScroll || false;
41940         
41941         
41942        
41943         
41944         this.duration = c.duration || .30;
41945         this.slideDuration = c.slideDuration || .45;
41946         this.config = c;
41947        
41948     },
41949     /**
41950      * Returns true if this region is currently visible.
41951      * @return {Boolean}
41952      */
41953     isVisible : function(){
41954         return this.visible;
41955     },
41956
41957     /**
41958      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41959      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41960      */
41961     //setCollapsedTitle : function(title){
41962     //    title = title || "&#160;";
41963      //   if(this.collapsedTitleTextEl){
41964       //      this.collapsedTitleTextEl.innerHTML = title;
41965        // }
41966     //},
41967
41968     getBox : function(){
41969         var b;
41970       //  if(!this.collapsed){
41971             b = this.el.getBox(false, true);
41972        // }else{
41973           //  b = this.collapsedEl.getBox(false, true);
41974         //}
41975         return b;
41976     },
41977
41978     getMargins : function(){
41979         return this.margins;
41980         //return this.collapsed ? this.cmargins : this.margins;
41981     },
41982 /*
41983     highlight : function(){
41984         this.el.addClass("x-layout-panel-dragover");
41985     },
41986
41987     unhighlight : function(){
41988         this.el.removeClass("x-layout-panel-dragover");
41989     },
41990 */
41991     updateBox : function(box)
41992     {
41993         if (!this.bodyEl) {
41994             return; // not rendered yet..
41995         }
41996         
41997         this.box = box;
41998         if(!this.collapsed){
41999             this.el.dom.style.left = box.x + "px";
42000             this.el.dom.style.top = box.y + "px";
42001             this.updateBody(box.width, box.height);
42002         }else{
42003             this.collapsedEl.dom.style.left = box.x + "px";
42004             this.collapsedEl.dom.style.top = box.y + "px";
42005             this.collapsedEl.setSize(box.width, box.height);
42006         }
42007         if(this.tabs){
42008             this.tabs.autoSizeTabs();
42009         }
42010     },
42011
42012     updateBody : function(w, h)
42013     {
42014         if(w !== null){
42015             this.el.setWidth(w);
42016             w -= this.el.getBorderWidth("rl");
42017             if(this.config.adjustments){
42018                 w += this.config.adjustments[0];
42019             }
42020         }
42021         if(h !== null && h > 0){
42022             this.el.setHeight(h);
42023             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42024             h -= this.el.getBorderWidth("tb");
42025             if(this.config.adjustments){
42026                 h += this.config.adjustments[1];
42027             }
42028             this.bodyEl.setHeight(h);
42029             if(this.tabs){
42030                 h = this.tabs.syncHeight(h);
42031             }
42032         }
42033         if(this.panelSize){
42034             w = w !== null ? w : this.panelSize.width;
42035             h = h !== null ? h : this.panelSize.height;
42036         }
42037         if(this.activePanel){
42038             var el = this.activePanel.getEl();
42039             w = w !== null ? w : el.getWidth();
42040             h = h !== null ? h : el.getHeight();
42041             this.panelSize = {width: w, height: h};
42042             this.activePanel.setSize(w, h);
42043         }
42044         if(Roo.isIE && this.tabs){
42045             this.tabs.el.repaint();
42046         }
42047     },
42048
42049     /**
42050      * Returns the container element for this region.
42051      * @return {Roo.Element}
42052      */
42053     getEl : function(){
42054         return this.el;
42055     },
42056
42057     /**
42058      * Hides this region.
42059      */
42060     hide : function(){
42061         //if(!this.collapsed){
42062             this.el.dom.style.left = "-2000px";
42063             this.el.hide();
42064         //}else{
42065          //   this.collapsedEl.dom.style.left = "-2000px";
42066          //   this.collapsedEl.hide();
42067        // }
42068         this.visible = false;
42069         this.fireEvent("visibilitychange", this, false);
42070     },
42071
42072     /**
42073      * Shows this region if it was previously hidden.
42074      */
42075     show : function(){
42076         //if(!this.collapsed){
42077             this.el.show();
42078         //}else{
42079         //    this.collapsedEl.show();
42080        // }
42081         this.visible = true;
42082         this.fireEvent("visibilitychange", this, true);
42083     },
42084 /*
42085     closeClicked : function(){
42086         if(this.activePanel){
42087             this.remove(this.activePanel);
42088         }
42089     },
42090
42091     collapseClick : function(e){
42092         if(this.isSlid){
42093            e.stopPropagation();
42094            this.slideIn();
42095         }else{
42096            e.stopPropagation();
42097            this.slideOut();
42098         }
42099     },
42100 */
42101     /**
42102      * Collapses this region.
42103      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42104      */
42105     /*
42106     collapse : function(skipAnim, skipCheck = false){
42107         if(this.collapsed) {
42108             return;
42109         }
42110         
42111         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42112             
42113             this.collapsed = true;
42114             if(this.split){
42115                 this.split.el.hide();
42116             }
42117             if(this.config.animate && skipAnim !== true){
42118                 this.fireEvent("invalidated", this);
42119                 this.animateCollapse();
42120             }else{
42121                 this.el.setLocation(-20000,-20000);
42122                 this.el.hide();
42123                 this.collapsedEl.show();
42124                 this.fireEvent("collapsed", this);
42125                 this.fireEvent("invalidated", this);
42126             }
42127         }
42128         
42129     },
42130 */
42131     animateCollapse : function(){
42132         // overridden
42133     },
42134
42135     /**
42136      * Expands this region if it was previously collapsed.
42137      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42138      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42139      */
42140     /*
42141     expand : function(e, skipAnim){
42142         if(e) {
42143             e.stopPropagation();
42144         }
42145         if(!this.collapsed || this.el.hasActiveFx()) {
42146             return;
42147         }
42148         if(this.isSlid){
42149             this.afterSlideIn();
42150             skipAnim = true;
42151         }
42152         this.collapsed = false;
42153         if(this.config.animate && skipAnim !== true){
42154             this.animateExpand();
42155         }else{
42156             this.el.show();
42157             if(this.split){
42158                 this.split.el.show();
42159             }
42160             this.collapsedEl.setLocation(-2000,-2000);
42161             this.collapsedEl.hide();
42162             this.fireEvent("invalidated", this);
42163             this.fireEvent("expanded", this);
42164         }
42165     },
42166 */
42167     animateExpand : function(){
42168         // overridden
42169     },
42170
42171     initTabs : function()
42172     {
42173         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42174         
42175         var ts = new Roo.bootstrap.panel.Tabs({
42176             el: this.bodyEl.dom,
42177             region : this,
42178             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42179             disableTooltips: this.config.disableTabTips,
42180             toolbar : this.config.toolbar
42181         });
42182         
42183         if(this.config.hideTabs){
42184             ts.stripWrap.setDisplayed(false);
42185         }
42186         this.tabs = ts;
42187         ts.resizeTabs = this.config.resizeTabs === true;
42188         ts.minTabWidth = this.config.minTabWidth || 40;
42189         ts.maxTabWidth = this.config.maxTabWidth || 250;
42190         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42191         ts.monitorResize = false;
42192         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42193         ts.bodyEl.addClass('roo-layout-tabs-body');
42194         this.panels.each(this.initPanelAsTab, this);
42195     },
42196
42197     initPanelAsTab : function(panel){
42198         var ti = this.tabs.addTab(
42199             panel.getEl().id,
42200             panel.getTitle(),
42201             null,
42202             this.config.closeOnTab && panel.isClosable(),
42203             panel.tpl
42204         );
42205         if(panel.tabTip !== undefined){
42206             ti.setTooltip(panel.tabTip);
42207         }
42208         ti.on("activate", function(){
42209               this.setActivePanel(panel);
42210         }, this);
42211         
42212         if(this.config.closeOnTab){
42213             ti.on("beforeclose", function(t, e){
42214                 e.cancel = true;
42215                 this.remove(panel);
42216             }, this);
42217         }
42218         
42219         panel.tabItem = ti;
42220         
42221         return ti;
42222     },
42223
42224     updatePanelTitle : function(panel, title)
42225     {
42226         if(this.activePanel == panel){
42227             this.updateTitle(title);
42228         }
42229         if(this.tabs){
42230             var ti = this.tabs.getTab(panel.getEl().id);
42231             ti.setText(title);
42232             if(panel.tabTip !== undefined){
42233                 ti.setTooltip(panel.tabTip);
42234             }
42235         }
42236     },
42237
42238     updateTitle : function(title){
42239         if(this.titleTextEl && !this.config.title){
42240             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42241         }
42242     },
42243
42244     setActivePanel : function(panel)
42245     {
42246         panel = this.getPanel(panel);
42247         if(this.activePanel && this.activePanel != panel){
42248             if(this.activePanel.setActiveState(false) === false){
42249                 return;
42250             }
42251         }
42252         this.activePanel = panel;
42253         panel.setActiveState(true);
42254         if(this.panelSize){
42255             panel.setSize(this.panelSize.width, this.panelSize.height);
42256         }
42257         if(this.closeBtn){
42258             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42259         }
42260         this.updateTitle(panel.getTitle());
42261         if(this.tabs){
42262             this.fireEvent("invalidated", this);
42263         }
42264         this.fireEvent("panelactivated", this, panel);
42265     },
42266
42267     /**
42268      * Shows the specified panel.
42269      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42270      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42271      */
42272     showPanel : function(panel)
42273     {
42274         panel = this.getPanel(panel);
42275         if(panel){
42276             if(this.tabs){
42277                 var tab = this.tabs.getTab(panel.getEl().id);
42278                 if(tab.isHidden()){
42279                     this.tabs.unhideTab(tab.id);
42280                 }
42281                 tab.activate();
42282             }else{
42283                 this.setActivePanel(panel);
42284             }
42285         }
42286         return panel;
42287     },
42288
42289     /**
42290      * Get the active panel for this region.
42291      * @return {Roo.ContentPanel} The active panel or null
42292      */
42293     getActivePanel : function(){
42294         return this.activePanel;
42295     },
42296
42297     validateVisibility : function(){
42298         if(this.panels.getCount() < 1){
42299             this.updateTitle("&#160;");
42300             this.closeBtn.hide();
42301             this.hide();
42302         }else{
42303             if(!this.isVisible()){
42304                 this.show();
42305             }
42306         }
42307     },
42308
42309     /**
42310      * Adds the passed ContentPanel(s) to this region.
42311      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42312      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42313      */
42314     add : function(panel)
42315     {
42316         if(arguments.length > 1){
42317             for(var i = 0, len = arguments.length; i < len; i++) {
42318                 this.add(arguments[i]);
42319             }
42320             return null;
42321         }
42322         
42323         // if we have not been rendered yet, then we can not really do much of this..
42324         if (!this.bodyEl) {
42325             this.unrendered_panels.push(panel);
42326             return panel;
42327         }
42328         
42329         
42330         
42331         
42332         if(this.hasPanel(panel)){
42333             this.showPanel(panel);
42334             return panel;
42335         }
42336         panel.setRegion(this);
42337         this.panels.add(panel);
42338        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42339             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42340             // and hide them... ???
42341             this.bodyEl.dom.appendChild(panel.getEl().dom);
42342             if(panel.background !== true){
42343                 this.setActivePanel(panel);
42344             }
42345             this.fireEvent("paneladded", this, panel);
42346             return panel;
42347         }
42348         */
42349         if(!this.tabs){
42350             this.initTabs();
42351         }else{
42352             this.initPanelAsTab(panel);
42353         }
42354         
42355         
42356         if(panel.background !== true){
42357             this.tabs.activate(panel.getEl().id);
42358         }
42359         this.fireEvent("paneladded", this, panel);
42360         return panel;
42361     },
42362
42363     /**
42364      * Hides the tab for the specified panel.
42365      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42366      */
42367     hidePanel : function(panel){
42368         if(this.tabs && (panel = this.getPanel(panel))){
42369             this.tabs.hideTab(panel.getEl().id);
42370         }
42371     },
42372
42373     /**
42374      * Unhides the tab for a previously hidden panel.
42375      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42376      */
42377     unhidePanel : function(panel){
42378         if(this.tabs && (panel = this.getPanel(panel))){
42379             this.tabs.unhideTab(panel.getEl().id);
42380         }
42381     },
42382
42383     clearPanels : function(){
42384         while(this.panels.getCount() > 0){
42385              this.remove(this.panels.first());
42386         }
42387     },
42388
42389     /**
42390      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42391      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42392      * @param {Boolean} preservePanel Overrides the config preservePanel option
42393      * @return {Roo.ContentPanel} The panel that was removed
42394      */
42395     remove : function(panel, preservePanel)
42396     {
42397         panel = this.getPanel(panel);
42398         if(!panel){
42399             return null;
42400         }
42401         var e = {};
42402         this.fireEvent("beforeremove", this, panel, e);
42403         if(e.cancel === true){
42404             return null;
42405         }
42406         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42407         var panelId = panel.getId();
42408         this.panels.removeKey(panelId);
42409         if(preservePanel){
42410             document.body.appendChild(panel.getEl().dom);
42411         }
42412         if(this.tabs){
42413             this.tabs.removeTab(panel.getEl().id);
42414         }else if (!preservePanel){
42415             this.bodyEl.dom.removeChild(panel.getEl().dom);
42416         }
42417         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42418             var p = this.panels.first();
42419             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42420             tempEl.appendChild(p.getEl().dom);
42421             this.bodyEl.update("");
42422             this.bodyEl.dom.appendChild(p.getEl().dom);
42423             tempEl = null;
42424             this.updateTitle(p.getTitle());
42425             this.tabs = null;
42426             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42427             this.setActivePanel(p);
42428         }
42429         panel.setRegion(null);
42430         if(this.activePanel == panel){
42431             this.activePanel = null;
42432         }
42433         if(this.config.autoDestroy !== false && preservePanel !== true){
42434             try{panel.destroy();}catch(e){}
42435         }
42436         this.fireEvent("panelremoved", this, panel);
42437         return panel;
42438     },
42439
42440     /**
42441      * Returns the TabPanel component used by this region
42442      * @return {Roo.TabPanel}
42443      */
42444     getTabs : function(){
42445         return this.tabs;
42446     },
42447
42448     createTool : function(parentEl, className){
42449         var btn = Roo.DomHelper.append(parentEl, {
42450             tag: "div",
42451             cls: "x-layout-tools-button",
42452             children: [ {
42453                 tag: "div",
42454                 cls: "roo-layout-tools-button-inner " + className,
42455                 html: "&#160;"
42456             }]
42457         }, true);
42458         btn.addClassOnOver("roo-layout-tools-button-over");
42459         return btn;
42460     }
42461 });/*
42462  * Based on:
42463  * Ext JS Library 1.1.1
42464  * Copyright(c) 2006-2007, Ext JS, LLC.
42465  *
42466  * Originally Released Under LGPL - original licence link has changed is not relivant.
42467  *
42468  * Fork - LGPL
42469  * <script type="text/javascript">
42470  */
42471  
42472
42473
42474 /**
42475  * @class Roo.SplitLayoutRegion
42476  * @extends Roo.LayoutRegion
42477  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42478  */
42479 Roo.bootstrap.layout.Split = function(config){
42480     this.cursor = config.cursor;
42481     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42482 };
42483
42484 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42485 {
42486     splitTip : "Drag to resize.",
42487     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42488     useSplitTips : false,
42489
42490     applyConfig : function(config){
42491         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42492     },
42493     
42494     onRender : function(ctr,pos) {
42495         
42496         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42497         if(!this.config.split){
42498             return;
42499         }
42500         if(!this.split){
42501             
42502             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42503                             tag: "div",
42504                             id: this.el.id + "-split",
42505                             cls: "roo-layout-split roo-layout-split-"+this.position,
42506                             html: "&#160;"
42507             });
42508             /** The SplitBar for this region 
42509             * @type Roo.SplitBar */
42510             // does not exist yet...
42511             Roo.log([this.position, this.orientation]);
42512             
42513             this.split = new Roo.bootstrap.SplitBar({
42514                 dragElement : splitEl,
42515                 resizingElement: this.el,
42516                 orientation : this.orientation
42517             });
42518             
42519             this.split.on("moved", this.onSplitMove, this);
42520             this.split.useShim = this.config.useShim === true;
42521             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42522             if(this.useSplitTips){
42523                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42524             }
42525             //if(config.collapsible){
42526             //    this.split.el.on("dblclick", this.collapse,  this);
42527             //}
42528         }
42529         if(typeof this.config.minSize != "undefined"){
42530             this.split.minSize = this.config.minSize;
42531         }
42532         if(typeof this.config.maxSize != "undefined"){
42533             this.split.maxSize = this.config.maxSize;
42534         }
42535         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42536             this.hideSplitter();
42537         }
42538         
42539     },
42540
42541     getHMaxSize : function(){
42542          var cmax = this.config.maxSize || 10000;
42543          var center = this.mgr.getRegion("center");
42544          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42545     },
42546
42547     getVMaxSize : function(){
42548          var cmax = this.config.maxSize || 10000;
42549          var center = this.mgr.getRegion("center");
42550          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42551     },
42552
42553     onSplitMove : function(split, newSize){
42554         this.fireEvent("resized", this, newSize);
42555     },
42556     
42557     /** 
42558      * Returns the {@link Roo.SplitBar} for this region.
42559      * @return {Roo.SplitBar}
42560      */
42561     getSplitBar : function(){
42562         return this.split;
42563     },
42564     
42565     hide : function(){
42566         this.hideSplitter();
42567         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42568     },
42569
42570     hideSplitter : function(){
42571         if(this.split){
42572             this.split.el.setLocation(-2000,-2000);
42573             this.split.el.hide();
42574         }
42575     },
42576
42577     show : function(){
42578         if(this.split){
42579             this.split.el.show();
42580         }
42581         Roo.bootstrap.layout.Split.superclass.show.call(this);
42582     },
42583     
42584     beforeSlide: function(){
42585         if(Roo.isGecko){// firefox overflow auto bug workaround
42586             this.bodyEl.clip();
42587             if(this.tabs) {
42588                 this.tabs.bodyEl.clip();
42589             }
42590             if(this.activePanel){
42591                 this.activePanel.getEl().clip();
42592                 
42593                 if(this.activePanel.beforeSlide){
42594                     this.activePanel.beforeSlide();
42595                 }
42596             }
42597         }
42598     },
42599     
42600     afterSlide : function(){
42601         if(Roo.isGecko){// firefox overflow auto bug workaround
42602             this.bodyEl.unclip();
42603             if(this.tabs) {
42604                 this.tabs.bodyEl.unclip();
42605             }
42606             if(this.activePanel){
42607                 this.activePanel.getEl().unclip();
42608                 if(this.activePanel.afterSlide){
42609                     this.activePanel.afterSlide();
42610                 }
42611             }
42612         }
42613     },
42614
42615     initAutoHide : function(){
42616         if(this.autoHide !== false){
42617             if(!this.autoHideHd){
42618                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42619                 this.autoHideHd = {
42620                     "mouseout": function(e){
42621                         if(!e.within(this.el, true)){
42622                             st.delay(500);
42623                         }
42624                     },
42625                     "mouseover" : function(e){
42626                         st.cancel();
42627                     },
42628                     scope : this
42629                 };
42630             }
42631             this.el.on(this.autoHideHd);
42632         }
42633     },
42634
42635     clearAutoHide : function(){
42636         if(this.autoHide !== false){
42637             this.el.un("mouseout", this.autoHideHd.mouseout);
42638             this.el.un("mouseover", this.autoHideHd.mouseover);
42639         }
42640     },
42641
42642     clearMonitor : function(){
42643         Roo.get(document).un("click", this.slideInIf, this);
42644     },
42645
42646     // these names are backwards but not changed for compat
42647     slideOut : function(){
42648         if(this.isSlid || this.el.hasActiveFx()){
42649             return;
42650         }
42651         this.isSlid = true;
42652         if(this.collapseBtn){
42653             this.collapseBtn.hide();
42654         }
42655         this.closeBtnState = this.closeBtn.getStyle('display');
42656         this.closeBtn.hide();
42657         if(this.stickBtn){
42658             this.stickBtn.show();
42659         }
42660         this.el.show();
42661         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42662         this.beforeSlide();
42663         this.el.setStyle("z-index", 10001);
42664         this.el.slideIn(this.getSlideAnchor(), {
42665             callback: function(){
42666                 this.afterSlide();
42667                 this.initAutoHide();
42668                 Roo.get(document).on("click", this.slideInIf, this);
42669                 this.fireEvent("slideshow", this);
42670             },
42671             scope: this,
42672             block: true
42673         });
42674     },
42675
42676     afterSlideIn : function(){
42677         this.clearAutoHide();
42678         this.isSlid = false;
42679         this.clearMonitor();
42680         this.el.setStyle("z-index", "");
42681         if(this.collapseBtn){
42682             this.collapseBtn.show();
42683         }
42684         this.closeBtn.setStyle('display', this.closeBtnState);
42685         if(this.stickBtn){
42686             this.stickBtn.hide();
42687         }
42688         this.fireEvent("slidehide", this);
42689     },
42690
42691     slideIn : function(cb){
42692         if(!this.isSlid || this.el.hasActiveFx()){
42693             Roo.callback(cb);
42694             return;
42695         }
42696         this.isSlid = false;
42697         this.beforeSlide();
42698         this.el.slideOut(this.getSlideAnchor(), {
42699             callback: function(){
42700                 this.el.setLeftTop(-10000, -10000);
42701                 this.afterSlide();
42702                 this.afterSlideIn();
42703                 Roo.callback(cb);
42704             },
42705             scope: this,
42706             block: true
42707         });
42708     },
42709     
42710     slideInIf : function(e){
42711         if(!e.within(this.el)){
42712             this.slideIn();
42713         }
42714     },
42715
42716     animateCollapse : function(){
42717         this.beforeSlide();
42718         this.el.setStyle("z-index", 20000);
42719         var anchor = this.getSlideAnchor();
42720         this.el.slideOut(anchor, {
42721             callback : function(){
42722                 this.el.setStyle("z-index", "");
42723                 this.collapsedEl.slideIn(anchor, {duration:.3});
42724                 this.afterSlide();
42725                 this.el.setLocation(-10000,-10000);
42726                 this.el.hide();
42727                 this.fireEvent("collapsed", this);
42728             },
42729             scope: this,
42730             block: true
42731         });
42732     },
42733
42734     animateExpand : function(){
42735         this.beforeSlide();
42736         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42737         this.el.setStyle("z-index", 20000);
42738         this.collapsedEl.hide({
42739             duration:.1
42740         });
42741         this.el.slideIn(this.getSlideAnchor(), {
42742             callback : function(){
42743                 this.el.setStyle("z-index", "");
42744                 this.afterSlide();
42745                 if(this.split){
42746                     this.split.el.show();
42747                 }
42748                 this.fireEvent("invalidated", this);
42749                 this.fireEvent("expanded", this);
42750             },
42751             scope: this,
42752             block: true
42753         });
42754     },
42755
42756     anchors : {
42757         "west" : "left",
42758         "east" : "right",
42759         "north" : "top",
42760         "south" : "bottom"
42761     },
42762
42763     sanchors : {
42764         "west" : "l",
42765         "east" : "r",
42766         "north" : "t",
42767         "south" : "b"
42768     },
42769
42770     canchors : {
42771         "west" : "tl-tr",
42772         "east" : "tr-tl",
42773         "north" : "tl-bl",
42774         "south" : "bl-tl"
42775     },
42776
42777     getAnchor : function(){
42778         return this.anchors[this.position];
42779     },
42780
42781     getCollapseAnchor : function(){
42782         return this.canchors[this.position];
42783     },
42784
42785     getSlideAnchor : function(){
42786         return this.sanchors[this.position];
42787     },
42788
42789     getAlignAdj : function(){
42790         var cm = this.cmargins;
42791         switch(this.position){
42792             case "west":
42793                 return [0, 0];
42794             break;
42795             case "east":
42796                 return [0, 0];
42797             break;
42798             case "north":
42799                 return [0, 0];
42800             break;
42801             case "south":
42802                 return [0, 0];
42803             break;
42804         }
42805     },
42806
42807     getExpandAdj : function(){
42808         var c = this.collapsedEl, cm = this.cmargins;
42809         switch(this.position){
42810             case "west":
42811                 return [-(cm.right+c.getWidth()+cm.left), 0];
42812             break;
42813             case "east":
42814                 return [cm.right+c.getWidth()+cm.left, 0];
42815             break;
42816             case "north":
42817                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42818             break;
42819             case "south":
42820                 return [0, cm.top+cm.bottom+c.getHeight()];
42821             break;
42822         }
42823     }
42824 });/*
42825  * Based on:
42826  * Ext JS Library 1.1.1
42827  * Copyright(c) 2006-2007, Ext JS, LLC.
42828  *
42829  * Originally Released Under LGPL - original licence link has changed is not relivant.
42830  *
42831  * Fork - LGPL
42832  * <script type="text/javascript">
42833  */
42834 /*
42835  * These classes are private internal classes
42836  */
42837 Roo.bootstrap.layout.Center = function(config){
42838     config.region = "center";
42839     Roo.bootstrap.layout.Region.call(this, config);
42840     this.visible = true;
42841     this.minWidth = config.minWidth || 20;
42842     this.minHeight = config.minHeight || 20;
42843 };
42844
42845 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42846     hide : function(){
42847         // center panel can't be hidden
42848     },
42849     
42850     show : function(){
42851         // center panel can't be hidden
42852     },
42853     
42854     getMinWidth: function(){
42855         return this.minWidth;
42856     },
42857     
42858     getMinHeight: function(){
42859         return this.minHeight;
42860     }
42861 });
42862
42863
42864
42865
42866  
42867
42868
42869
42870
42871
42872
42873 Roo.bootstrap.layout.North = function(config)
42874 {
42875     config.region = 'north';
42876     config.cursor = 'n-resize';
42877     
42878     Roo.bootstrap.layout.Split.call(this, config);
42879     
42880     
42881     if(this.split){
42882         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42883         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42884         this.split.el.addClass("roo-layout-split-v");
42885     }
42886     //var size = config.initialSize || config.height;
42887     //if(this.el && typeof size != "undefined"){
42888     //    this.el.setHeight(size);
42889     //}
42890 };
42891 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42892 {
42893     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42894      
42895      
42896     onRender : function(ctr, pos)
42897     {
42898         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42899         var size = this.config.initialSize || this.config.height;
42900         if(this.el && typeof size != "undefined"){
42901             this.el.setHeight(size);
42902         }
42903     
42904     },
42905     
42906     getBox : function(){
42907         if(this.collapsed){
42908             return this.collapsedEl.getBox();
42909         }
42910         var box = this.el.getBox();
42911         if(this.split){
42912             box.height += this.split.el.getHeight();
42913         }
42914         return box;
42915     },
42916     
42917     updateBox : function(box){
42918         if(this.split && !this.collapsed){
42919             box.height -= this.split.el.getHeight();
42920             this.split.el.setLeft(box.x);
42921             this.split.el.setTop(box.y+box.height);
42922             this.split.el.setWidth(box.width);
42923         }
42924         if(this.collapsed){
42925             this.updateBody(box.width, null);
42926         }
42927         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42928     }
42929 });
42930
42931
42932
42933
42934
42935 Roo.bootstrap.layout.South = function(config){
42936     config.region = 'south';
42937     config.cursor = 's-resize';
42938     Roo.bootstrap.layout.Split.call(this, config);
42939     if(this.split){
42940         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42941         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42942         this.split.el.addClass("roo-layout-split-v");
42943     }
42944     
42945 };
42946
42947 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42948     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42949     
42950     onRender : function(ctr, pos)
42951     {
42952         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42953         var size = this.config.initialSize || this.config.height;
42954         if(this.el && typeof size != "undefined"){
42955             this.el.setHeight(size);
42956         }
42957     
42958     },
42959     
42960     getBox : function(){
42961         if(this.collapsed){
42962             return this.collapsedEl.getBox();
42963         }
42964         var box = this.el.getBox();
42965         if(this.split){
42966             var sh = this.split.el.getHeight();
42967             box.height += sh;
42968             box.y -= sh;
42969         }
42970         return box;
42971     },
42972     
42973     updateBox : function(box){
42974         if(this.split && !this.collapsed){
42975             var sh = this.split.el.getHeight();
42976             box.height -= sh;
42977             box.y += sh;
42978             this.split.el.setLeft(box.x);
42979             this.split.el.setTop(box.y-sh);
42980             this.split.el.setWidth(box.width);
42981         }
42982         if(this.collapsed){
42983             this.updateBody(box.width, null);
42984         }
42985         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42986     }
42987 });
42988
42989 Roo.bootstrap.layout.East = function(config){
42990     config.region = "east";
42991     config.cursor = "e-resize";
42992     Roo.bootstrap.layout.Split.call(this, config);
42993     if(this.split){
42994         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42995         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42996         this.split.el.addClass("roo-layout-split-h");
42997     }
42998     
42999 };
43000 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43001     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43002     
43003     onRender : function(ctr, pos)
43004     {
43005         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43006         var size = this.config.initialSize || this.config.width;
43007         if(this.el && typeof size != "undefined"){
43008             this.el.setWidth(size);
43009         }
43010     
43011     },
43012     
43013     getBox : function(){
43014         if(this.collapsed){
43015             return this.collapsedEl.getBox();
43016         }
43017         var box = this.el.getBox();
43018         if(this.split){
43019             var sw = this.split.el.getWidth();
43020             box.width += sw;
43021             box.x -= sw;
43022         }
43023         return box;
43024     },
43025
43026     updateBox : function(box){
43027         if(this.split && !this.collapsed){
43028             var sw = this.split.el.getWidth();
43029             box.width -= sw;
43030             this.split.el.setLeft(box.x);
43031             this.split.el.setTop(box.y);
43032             this.split.el.setHeight(box.height);
43033             box.x += sw;
43034         }
43035         if(this.collapsed){
43036             this.updateBody(null, box.height);
43037         }
43038         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43039     }
43040 });
43041
43042 Roo.bootstrap.layout.West = function(config){
43043     config.region = "west";
43044     config.cursor = "w-resize";
43045     
43046     Roo.bootstrap.layout.Split.call(this, config);
43047     if(this.split){
43048         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43049         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43050         this.split.el.addClass("roo-layout-split-h");
43051     }
43052     
43053 };
43054 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43055     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43056     
43057     onRender: function(ctr, pos)
43058     {
43059         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43060         var size = this.config.initialSize || this.config.width;
43061         if(typeof size != "undefined"){
43062             this.el.setWidth(size);
43063         }
43064     },
43065     
43066     getBox : function(){
43067         if(this.collapsed){
43068             return this.collapsedEl.getBox();
43069         }
43070         var box = this.el.getBox();
43071         if (box.width == 0) {
43072             box.width = this.config.width; // kludge?
43073         }
43074         if(this.split){
43075             box.width += this.split.el.getWidth();
43076         }
43077         return box;
43078     },
43079     
43080     updateBox : function(box){
43081         if(this.split && !this.collapsed){
43082             var sw = this.split.el.getWidth();
43083             box.width -= sw;
43084             this.split.el.setLeft(box.x+box.width);
43085             this.split.el.setTop(box.y);
43086             this.split.el.setHeight(box.height);
43087         }
43088         if(this.collapsed){
43089             this.updateBody(null, box.height);
43090         }
43091         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43092     }
43093 });/*
43094  * Based on:
43095  * Ext JS Library 1.1.1
43096  * Copyright(c) 2006-2007, Ext JS, LLC.
43097  *
43098  * Originally Released Under LGPL - original licence link has changed is not relivant.
43099  *
43100  * Fork - LGPL
43101  * <script type="text/javascript">
43102  */
43103 /**
43104  * @class Roo.bootstrap.paenl.Content
43105  * @extends Roo.util.Observable
43106  * @children Roo.bootstrap.Component
43107  * @parent builder Roo.bootstrap.layout.Border
43108  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43109  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43110  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43111  * @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
43112  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43113  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43114  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43115  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43116  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43117  * @cfg {String} title          The title for this panel
43118  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43119  * @cfg {String} url            Calls {@link #setUrl} with this value
43120  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43121  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43122  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43123  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43124  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43125  * @cfg {Boolean} badges render the badges
43126  * @cfg {String} cls  extra classes to use  
43127  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43128  
43129  * @constructor
43130  * Create a new ContentPanel.
43131  * @param {String/Object} config A string to set only the title or a config object
43132  
43133  */
43134 Roo.bootstrap.panel.Content = function( config){
43135     
43136     this.tpl = config.tpl || false;
43137     
43138     var el = config.el;
43139     var content = config.content;
43140
43141     if(config.autoCreate){ // xtype is available if this is called from factory
43142         el = Roo.id();
43143     }
43144     this.el = Roo.get(el);
43145     if(!this.el && config && config.autoCreate){
43146         if(typeof config.autoCreate == "object"){
43147             if(!config.autoCreate.id){
43148                 config.autoCreate.id = config.id||el;
43149             }
43150             this.el = Roo.DomHelper.append(document.body,
43151                         config.autoCreate, true);
43152         }else{
43153             var elcfg =  {
43154                 tag: "div",
43155                 cls: (config.cls || '') +
43156                     (config.background ? ' bg-' + config.background : '') +
43157                     " roo-layout-inactive-content",
43158                 id: config.id||el
43159             };
43160             if (config.iframe) {
43161                 elcfg.cn = [
43162                     {
43163                         tag : 'iframe',
43164                         style : 'border: 0px',
43165                         src : 'about:blank'
43166                     }
43167                 ];
43168             }
43169               
43170             if (config.html) {
43171                 elcfg.html = config.html;
43172                 
43173             }
43174                         
43175             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43176             if (config.iframe) {
43177                 this.iframeEl = this.el.select('iframe',true).first();
43178             }
43179             
43180         }
43181     } 
43182     this.closable = false;
43183     this.loaded = false;
43184     this.active = false;
43185    
43186       
43187     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43188         
43189         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43190         
43191         this.wrapEl = this.el; //this.el.wrap();
43192         var ti = [];
43193         if (config.toolbar.items) {
43194             ti = config.toolbar.items ;
43195             delete config.toolbar.items ;
43196         }
43197         
43198         var nitems = [];
43199         this.toolbar.render(this.wrapEl, 'before');
43200         for(var i =0;i < ti.length;i++) {
43201           //  Roo.log(['add child', items[i]]);
43202             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43203         }
43204         this.toolbar.items = nitems;
43205         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43206         delete config.toolbar;
43207         
43208     }
43209     /*
43210     // xtype created footer. - not sure if will work as we normally have to render first..
43211     if (this.footer && !this.footer.el && this.footer.xtype) {
43212         if (!this.wrapEl) {
43213             this.wrapEl = this.el.wrap();
43214         }
43215     
43216         this.footer.container = this.wrapEl.createChild();
43217          
43218         this.footer = Roo.factory(this.footer, Roo);
43219         
43220     }
43221     */
43222     
43223      if(typeof config == "string"){
43224         this.title = config;
43225     }else{
43226         Roo.apply(this, config);
43227     }
43228     
43229     if(this.resizeEl){
43230         this.resizeEl = Roo.get(this.resizeEl, true);
43231     }else{
43232         this.resizeEl = this.el;
43233     }
43234     // handle view.xtype
43235     
43236  
43237     
43238     
43239     this.addEvents({
43240         /**
43241          * @event activate
43242          * Fires when this panel is activated. 
43243          * @param {Roo.ContentPanel} this
43244          */
43245         "activate" : true,
43246         /**
43247          * @event deactivate
43248          * Fires when this panel is activated. 
43249          * @param {Roo.ContentPanel} this
43250          */
43251         "deactivate" : true,
43252
43253         /**
43254          * @event resize
43255          * Fires when this panel is resized if fitToFrame is true.
43256          * @param {Roo.ContentPanel} this
43257          * @param {Number} width The width after any component adjustments
43258          * @param {Number} height The height after any component adjustments
43259          */
43260         "resize" : true,
43261         
43262          /**
43263          * @event render
43264          * Fires when this tab is created
43265          * @param {Roo.ContentPanel} this
43266          */
43267         "render" : true,
43268         
43269           /**
43270          * @event scroll
43271          * Fires when this content is scrolled
43272          * @param {Roo.ContentPanel} this
43273          * @param {Event} scrollEvent
43274          */
43275         "scroll" : true
43276         
43277         
43278         
43279     });
43280     
43281
43282     
43283     
43284     if(this.autoScroll && !this.iframe){
43285         this.resizeEl.setStyle("overflow", "auto");
43286         this.resizeEl.on('scroll', this.onScroll, this);
43287     } else {
43288         // fix randome scrolling
43289         //this.el.on('scroll', function() {
43290         //    Roo.log('fix random scolling');
43291         //    this.scrollTo('top',0); 
43292         //});
43293     }
43294     content = content || this.content;
43295     if(content){
43296         this.setContent(content);
43297     }
43298     if(config && config.url){
43299         this.setUrl(this.url, this.params, this.loadOnce);
43300     }
43301     
43302     
43303     
43304     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43305     
43306     if (this.view && typeof(this.view.xtype) != 'undefined') {
43307         this.view.el = this.el.appendChild(document.createElement("div"));
43308         this.view = Roo.factory(this.view); 
43309         this.view.render  &&  this.view.render(false, '');  
43310     }
43311     
43312     
43313     this.fireEvent('render', this);
43314 };
43315
43316 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43317     
43318     cls : '',
43319     background : '',
43320     
43321     tabTip : '',
43322     
43323     iframe : false,
43324     iframeEl : false,
43325     
43326     /* Resize Element - use this to work out scroll etc. */
43327     resizeEl : false,
43328     
43329     setRegion : function(region){
43330         this.region = region;
43331         this.setActiveClass(region && !this.background);
43332     },
43333     
43334     
43335     setActiveClass: function(state)
43336     {
43337         if(state){
43338            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43339            this.el.setStyle('position','relative');
43340         }else{
43341            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43342            this.el.setStyle('position', 'absolute');
43343         } 
43344     },
43345     
43346     /**
43347      * Returns the toolbar for this Panel if one was configured. 
43348      * @return {Roo.Toolbar} 
43349      */
43350     getToolbar : function(){
43351         return this.toolbar;
43352     },
43353     
43354     setActiveState : function(active)
43355     {
43356         this.active = active;
43357         this.setActiveClass(active);
43358         if(!active){
43359             if(this.fireEvent("deactivate", this) === false){
43360                 return false;
43361             }
43362             return true;
43363         }
43364         this.fireEvent("activate", this);
43365         return true;
43366     },
43367     /**
43368      * Updates this panel's element (not for iframe)
43369      * @param {String} content The new content
43370      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43371     */
43372     setContent : function(content, loadScripts){
43373         if (this.iframe) {
43374             return;
43375         }
43376         
43377         this.el.update(content, loadScripts);
43378     },
43379
43380     ignoreResize : function(w, h)
43381     {
43382         //return false; // always resize?
43383         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43384             return true;
43385         }else{
43386             this.lastSize = {width: w, height: h};
43387             return false;
43388         }
43389     },
43390     /**
43391      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43392      * @return {Roo.UpdateManager} The UpdateManager
43393      */
43394     getUpdateManager : function(){
43395         if (this.iframe) {
43396             return false;
43397         }
43398         return this.el.getUpdateManager();
43399     },
43400      /**
43401      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43402      * Does not work with IFRAME contents
43403      * @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:
43404 <pre><code>
43405 panel.load({
43406     url: "your-url.php",
43407     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43408     callback: yourFunction,
43409     scope: yourObject, //(optional scope)
43410     discardUrl: false,
43411     nocache: false,
43412     text: "Loading...",
43413     timeout: 30,
43414     scripts: false
43415 });
43416 </code></pre>
43417      
43418      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43419      * 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.
43420      * @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}
43421      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43422      * @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.
43423      * @return {Roo.ContentPanel} this
43424      */
43425     load : function(){
43426         
43427         if (this.iframe) {
43428             return this;
43429         }
43430         
43431         var um = this.el.getUpdateManager();
43432         um.update.apply(um, arguments);
43433         return this;
43434     },
43435
43436
43437     /**
43438      * 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.
43439      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43440      * @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)
43441      * @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)
43442      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43443      */
43444     setUrl : function(url, params, loadOnce){
43445         if (this.iframe) {
43446             this.iframeEl.dom.src = url;
43447             return false;
43448         }
43449         
43450         if(this.refreshDelegate){
43451             this.removeListener("activate", this.refreshDelegate);
43452         }
43453         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43454         this.on("activate", this.refreshDelegate);
43455         return this.el.getUpdateManager();
43456     },
43457     
43458     _handleRefresh : function(url, params, loadOnce){
43459         if(!loadOnce || !this.loaded){
43460             var updater = this.el.getUpdateManager();
43461             updater.update(url, params, this._setLoaded.createDelegate(this));
43462         }
43463     },
43464     
43465     _setLoaded : function(){
43466         this.loaded = true;
43467     }, 
43468     
43469     /**
43470      * Returns this panel's id
43471      * @return {String} 
43472      */
43473     getId : function(){
43474         return this.el.id;
43475     },
43476     
43477     /** 
43478      * Returns this panel's element - used by regiosn to add.
43479      * @return {Roo.Element} 
43480      */
43481     getEl : function(){
43482         return this.wrapEl || this.el;
43483     },
43484     
43485    
43486     
43487     adjustForComponents : function(width, height)
43488     {
43489         //Roo.log('adjustForComponents ');
43490         if(this.resizeEl != this.el){
43491             width -= this.el.getFrameWidth('lr');
43492             height -= this.el.getFrameWidth('tb');
43493         }
43494         if(this.toolbar){
43495             var te = this.toolbar.getEl();
43496             te.setWidth(width);
43497             height -= te.getHeight();
43498         }
43499         if(this.footer){
43500             var te = this.footer.getEl();
43501             te.setWidth(width);
43502             height -= te.getHeight();
43503         }
43504         
43505         
43506         if(this.adjustments){
43507             width += this.adjustments[0];
43508             height += this.adjustments[1];
43509         }
43510         return {"width": width, "height": height};
43511     },
43512     
43513     setSize : function(width, height){
43514         if(this.fitToFrame && !this.ignoreResize(width, height)){
43515             if(this.fitContainer && this.resizeEl != this.el){
43516                 this.el.setSize(width, height);
43517             }
43518             var size = this.adjustForComponents(width, height);
43519             if (this.iframe) {
43520                 this.iframeEl.setSize(width,height);
43521             }
43522             
43523             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43524             this.fireEvent('resize', this, size.width, size.height);
43525             
43526             
43527         }
43528     },
43529     
43530     /**
43531      * Returns this panel's title
43532      * @return {String} 
43533      */
43534     getTitle : function(){
43535         
43536         if (typeof(this.title) != 'object') {
43537             return this.title;
43538         }
43539         
43540         var t = '';
43541         for (var k in this.title) {
43542             if (!this.title.hasOwnProperty(k)) {
43543                 continue;
43544             }
43545             
43546             if (k.indexOf('-') >= 0) {
43547                 var s = k.split('-');
43548                 for (var i = 0; i<s.length; i++) {
43549                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43550                 }
43551             } else {
43552                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43553             }
43554         }
43555         return t;
43556     },
43557     
43558     /**
43559      * Set this panel's title
43560      * @param {String} title
43561      */
43562     setTitle : function(title){
43563         this.title = title;
43564         if(this.region){
43565             this.region.updatePanelTitle(this, title);
43566         }
43567     },
43568     
43569     /**
43570      * Returns true is this panel was configured to be closable
43571      * @return {Boolean} 
43572      */
43573     isClosable : function(){
43574         return this.closable;
43575     },
43576     
43577     beforeSlide : function(){
43578         this.el.clip();
43579         this.resizeEl.clip();
43580     },
43581     
43582     afterSlide : function(){
43583         this.el.unclip();
43584         this.resizeEl.unclip();
43585     },
43586     
43587     /**
43588      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43589      *   Will fail silently if the {@link #setUrl} method has not been called.
43590      *   This does not activate the panel, just updates its content.
43591      */
43592     refresh : function(){
43593         if(this.refreshDelegate){
43594            this.loaded = false;
43595            this.refreshDelegate();
43596         }
43597     },
43598     
43599     /**
43600      * Destroys this panel
43601      */
43602     destroy : function(){
43603         this.el.removeAllListeners();
43604         var tempEl = document.createElement("span");
43605         tempEl.appendChild(this.el.dom);
43606         tempEl.innerHTML = "";
43607         this.el.remove();
43608         this.el = null;
43609     },
43610     
43611     /**
43612      * form - if the content panel contains a form - this is a reference to it.
43613      * @type {Roo.form.Form}
43614      */
43615     form : false,
43616     /**
43617      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43618      *    This contains a reference to it.
43619      * @type {Roo.View}
43620      */
43621     view : false,
43622     
43623       /**
43624      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43625      * <pre><code>
43626
43627 layout.addxtype({
43628        xtype : 'Form',
43629        items: [ .... ]
43630    }
43631 );
43632
43633 </code></pre>
43634      * @param {Object} cfg Xtype definition of item to add.
43635      */
43636     
43637     
43638     getChildContainer: function () {
43639         return this.getEl();
43640     },
43641     
43642     
43643     onScroll : function(e)
43644     {
43645         this.fireEvent('scroll', this, e);
43646     }
43647     
43648     
43649     /*
43650         var  ret = new Roo.factory(cfg);
43651         return ret;
43652         
43653         
43654         // add form..
43655         if (cfg.xtype.match(/^Form$/)) {
43656             
43657             var el;
43658             //if (this.footer) {
43659             //    el = this.footer.container.insertSibling(false, 'before');
43660             //} else {
43661                 el = this.el.createChild();
43662             //}
43663
43664             this.form = new  Roo.form.Form(cfg);
43665             
43666             
43667             if ( this.form.allItems.length) {
43668                 this.form.render(el.dom);
43669             }
43670             return this.form;
43671         }
43672         // should only have one of theses..
43673         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43674             // views.. should not be just added - used named prop 'view''
43675             
43676             cfg.el = this.el.appendChild(document.createElement("div"));
43677             // factory?
43678             
43679             var ret = new Roo.factory(cfg);
43680              
43681              ret.render && ret.render(false, ''); // render blank..
43682             this.view = ret;
43683             return ret;
43684         }
43685         return false;
43686     }
43687     \*/
43688 });
43689  
43690 /**
43691  * @class Roo.bootstrap.panel.Grid
43692  * @extends Roo.bootstrap.panel.Content
43693  * @constructor
43694  * Create a new GridPanel.
43695  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43696  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43697  * @param {Object} config A the config object
43698   
43699  */
43700
43701
43702
43703 Roo.bootstrap.panel.Grid = function(config)
43704 {
43705     
43706       
43707     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43708         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43709
43710     config.el = this.wrapper;
43711     //this.el = this.wrapper;
43712     
43713       if (config.container) {
43714         // ctor'ed from a Border/panel.grid
43715         
43716         
43717         this.wrapper.setStyle("overflow", "hidden");
43718         this.wrapper.addClass('roo-grid-container');
43719
43720     }
43721     
43722     
43723     if(config.toolbar){
43724         var tool_el = this.wrapper.createChild();    
43725         this.toolbar = Roo.factory(config.toolbar);
43726         var ti = [];
43727         if (config.toolbar.items) {
43728             ti = config.toolbar.items ;
43729             delete config.toolbar.items ;
43730         }
43731         
43732         var nitems = [];
43733         this.toolbar.render(tool_el);
43734         for(var i =0;i < ti.length;i++) {
43735           //  Roo.log(['add child', items[i]]);
43736             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43737         }
43738         this.toolbar.items = nitems;
43739         
43740         delete config.toolbar;
43741     }
43742     
43743     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43744     config.grid.scrollBody = true;;
43745     config.grid.monitorWindowResize = false; // turn off autosizing
43746     config.grid.autoHeight = false;
43747     config.grid.autoWidth = false;
43748     
43749     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43750     
43751     if (config.background) {
43752         // render grid on panel activation (if panel background)
43753         this.on('activate', function(gp) {
43754             if (!gp.grid.rendered) {
43755                 gp.grid.render(this.wrapper);
43756                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43757             }
43758         });
43759             
43760     } else {
43761         this.grid.render(this.wrapper);
43762         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43763
43764     }
43765     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43766     // ??? needed ??? config.el = this.wrapper;
43767     
43768     
43769     
43770   
43771     // xtype created footer. - not sure if will work as we normally have to render first..
43772     if (this.footer && !this.footer.el && this.footer.xtype) {
43773         
43774         var ctr = this.grid.getView().getFooterPanel(true);
43775         this.footer.dataSource = this.grid.dataSource;
43776         this.footer = Roo.factory(this.footer, Roo);
43777         this.footer.render(ctr);
43778         
43779     }
43780     
43781     
43782     
43783     
43784      
43785 };
43786
43787 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43788 {
43789   
43790     getId : function(){
43791         return this.grid.id;
43792     },
43793     
43794     /**
43795      * Returns the grid for this panel
43796      * @return {Roo.bootstrap.Table} 
43797      */
43798     getGrid : function(){
43799         return this.grid;    
43800     },
43801     
43802     setSize : function(width, height)
43803     {
43804      
43805         //if(!this.ignoreResize(width, height)){
43806             var grid = this.grid;
43807             var size = this.adjustForComponents(width, height);
43808             // tfoot is not a footer?
43809           
43810             
43811             var gridel = grid.getGridEl();
43812             gridel.setSize(size.width, size.height);
43813             
43814             var tbd = grid.getGridEl().select('tbody', true).first();
43815             var thd = grid.getGridEl().select('thead',true).first();
43816             var tbf= grid.getGridEl().select('tfoot', true).first();
43817
43818             if (tbf) {
43819                 size.height -= tbf.getHeight();
43820             }
43821             if (thd) {
43822                 size.height -= thd.getHeight();
43823             }
43824             
43825             tbd.setSize(size.width, size.height );
43826             // this is for the account management tab -seems to work there.
43827             var thd = grid.getGridEl().select('thead',true).first();
43828             //if (tbd) {
43829             //    tbd.setSize(size.width, size.height - thd.getHeight());
43830             //}
43831              
43832             grid.autoSize();
43833         //}
43834    
43835     },
43836      
43837     
43838     
43839     beforeSlide : function(){
43840         this.grid.getView().scroller.clip();
43841     },
43842     
43843     afterSlide : function(){
43844         this.grid.getView().scroller.unclip();
43845     },
43846     
43847     destroy : function(){
43848         this.grid.destroy();
43849         delete this.grid;
43850         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43851     }
43852 });
43853
43854 /**
43855  * @class Roo.bootstrap.panel.Nest
43856  * @extends Roo.bootstrap.panel.Content
43857  * @constructor
43858  * Create a new Panel, that can contain a layout.Border.
43859  * 
43860  * 
43861  * @param {String/Object} config A string to set only the title or a config object
43862  */
43863 Roo.bootstrap.panel.Nest = function(config)
43864 {
43865     // construct with only one argument..
43866     /* FIXME - implement nicer consturctors
43867     if (layout.layout) {
43868         config = layout;
43869         layout = config.layout;
43870         delete config.layout;
43871     }
43872     if (layout.xtype && !layout.getEl) {
43873         // then layout needs constructing..
43874         layout = Roo.factory(layout, Roo);
43875     }
43876     */
43877     
43878     config.el =  config.layout.getEl();
43879     
43880     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43881     
43882     config.layout.monitorWindowResize = false; // turn off autosizing
43883     this.layout = config.layout;
43884     this.layout.getEl().addClass("roo-layout-nested-layout");
43885     this.layout.parent = this;
43886     
43887     
43888     
43889     
43890 };
43891
43892 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43893     /**
43894     * @cfg {Roo.BorderLayout} layout The layout for this panel
43895     */
43896     layout : false,
43897
43898     setSize : function(width, height){
43899         if(!this.ignoreResize(width, height)){
43900             var size = this.adjustForComponents(width, height);
43901             var el = this.layout.getEl();
43902             if (size.height < 1) {
43903                 el.setWidth(size.width);   
43904             } else {
43905                 el.setSize(size.width, size.height);
43906             }
43907             var touch = el.dom.offsetWidth;
43908             this.layout.layout();
43909             // ie requires a double layout on the first pass
43910             if(Roo.isIE && !this.initialized){
43911                 this.initialized = true;
43912                 this.layout.layout();
43913             }
43914         }
43915     },
43916     
43917     // activate all subpanels if not currently active..
43918     
43919     setActiveState : function(active){
43920         this.active = active;
43921         this.setActiveClass(active);
43922         
43923         if(!active){
43924             this.fireEvent("deactivate", this);
43925             return;
43926         }
43927         
43928         this.fireEvent("activate", this);
43929         // not sure if this should happen before or after..
43930         if (!this.layout) {
43931             return; // should not happen..
43932         }
43933         var reg = false;
43934         for (var r in this.layout.regions) {
43935             reg = this.layout.getRegion(r);
43936             if (reg.getActivePanel()) {
43937                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43938                 reg.setActivePanel(reg.getActivePanel());
43939                 continue;
43940             }
43941             if (!reg.panels.length) {
43942                 continue;
43943             }
43944             reg.showPanel(reg.getPanel(0));
43945         }
43946         
43947         
43948         
43949         
43950     },
43951     
43952     /**
43953      * Returns the nested BorderLayout for this panel
43954      * @return {Roo.BorderLayout} 
43955      */
43956     getLayout : function(){
43957         return this.layout;
43958     },
43959     
43960      /**
43961      * Adds a xtype elements to the layout of the nested panel
43962      * <pre><code>
43963
43964 panel.addxtype({
43965        xtype : 'ContentPanel',
43966        region: 'west',
43967        items: [ .... ]
43968    }
43969 );
43970
43971 panel.addxtype({
43972         xtype : 'NestedLayoutPanel',
43973         region: 'west',
43974         layout: {
43975            center: { },
43976            west: { }   
43977         },
43978         items : [ ... list of content panels or nested layout panels.. ]
43979    }
43980 );
43981 </code></pre>
43982      * @param {Object} cfg Xtype definition of item to add.
43983      */
43984     addxtype : function(cfg) {
43985         return this.layout.addxtype(cfg);
43986     
43987     }
43988 });/*
43989  * Based on:
43990  * Ext JS Library 1.1.1
43991  * Copyright(c) 2006-2007, Ext JS, LLC.
43992  *
43993  * Originally Released Under LGPL - original licence link has changed is not relivant.
43994  *
43995  * Fork - LGPL
43996  * <script type="text/javascript">
43997  */
43998 /**
43999  * @class Roo.TabPanel
44000  * @extends Roo.util.Observable
44001  * A lightweight tab container.
44002  * <br><br>
44003  * Usage:
44004  * <pre><code>
44005 // basic tabs 1, built from existing content
44006 var tabs = new Roo.TabPanel("tabs1");
44007 tabs.addTab("script", "View Script");
44008 tabs.addTab("markup", "View Markup");
44009 tabs.activate("script");
44010
44011 // more advanced tabs, built from javascript
44012 var jtabs = new Roo.TabPanel("jtabs");
44013 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44014
44015 // set up the UpdateManager
44016 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44017 var updater = tab2.getUpdateManager();
44018 updater.setDefaultUrl("ajax1.htm");
44019 tab2.on('activate', updater.refresh, updater, true);
44020
44021 // Use setUrl for Ajax loading
44022 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44023 tab3.setUrl("ajax2.htm", null, true);
44024
44025 // Disabled tab
44026 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44027 tab4.disable();
44028
44029 jtabs.activate("jtabs-1");
44030  * </code></pre>
44031  * @constructor
44032  * Create a new TabPanel.
44033  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44034  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44035  */
44036 Roo.bootstrap.panel.Tabs = function(config){
44037     /**
44038     * The container element for this TabPanel.
44039     * @type Roo.Element
44040     */
44041     this.el = Roo.get(config.el);
44042     delete config.el;
44043     if(config){
44044         if(typeof config == "boolean"){
44045             this.tabPosition = config ? "bottom" : "top";
44046         }else{
44047             Roo.apply(this, config);
44048         }
44049     }
44050     
44051     if(this.tabPosition == "bottom"){
44052         // if tabs are at the bottom = create the body first.
44053         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44054         this.el.addClass("roo-tabs-bottom");
44055     }
44056     // next create the tabs holders
44057     
44058     if (this.tabPosition == "west"){
44059         
44060         var reg = this.region; // fake it..
44061         while (reg) {
44062             if (!reg.mgr.parent) {
44063                 break;
44064             }
44065             reg = reg.mgr.parent.region;
44066         }
44067         Roo.log("got nest?");
44068         Roo.log(reg);
44069         if (reg.mgr.getRegion('west')) {
44070             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44071             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44072             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44073             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44074             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44075         
44076             
44077         }
44078         
44079         
44080     } else {
44081      
44082         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44083         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44084         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44085         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44086     }
44087     
44088     
44089     if(Roo.isIE){
44090         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44091     }
44092     
44093     // finally - if tabs are at the top, then create the body last..
44094     if(this.tabPosition != "bottom"){
44095         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44096          * @type Roo.Element
44097          */
44098         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44099         this.el.addClass("roo-tabs-top");
44100     }
44101     this.items = [];
44102
44103     this.bodyEl.setStyle("position", "relative");
44104
44105     this.active = null;
44106     this.activateDelegate = this.activate.createDelegate(this);
44107
44108     this.addEvents({
44109         /**
44110          * @event tabchange
44111          * Fires when the active tab changes
44112          * @param {Roo.TabPanel} this
44113          * @param {Roo.TabPanelItem} activePanel The new active tab
44114          */
44115         "tabchange": true,
44116         /**
44117          * @event beforetabchange
44118          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44119          * @param {Roo.TabPanel} this
44120          * @param {Object} e Set cancel to true on this object to cancel the tab change
44121          * @param {Roo.TabPanelItem} tab The tab being changed to
44122          */
44123         "beforetabchange" : true
44124     });
44125
44126     Roo.EventManager.onWindowResize(this.onResize, this);
44127     this.cpad = this.el.getPadding("lr");
44128     this.hiddenCount = 0;
44129
44130
44131     // toolbar on the tabbar support...
44132     if (this.toolbar) {
44133         alert("no toolbar support yet");
44134         this.toolbar  = false;
44135         /*
44136         var tcfg = this.toolbar;
44137         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44138         this.toolbar = new Roo.Toolbar(tcfg);
44139         if (Roo.isSafari) {
44140             var tbl = tcfg.container.child('table', true);
44141             tbl.setAttribute('width', '100%');
44142         }
44143         */
44144         
44145     }
44146    
44147
44148
44149     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44150 };
44151
44152 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44153     /*
44154      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44155      */
44156     tabPosition : "top",
44157     /*
44158      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44159      */
44160     currentTabWidth : 0,
44161     /*
44162      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44163      */
44164     minTabWidth : 40,
44165     /*
44166      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44167      */
44168     maxTabWidth : 250,
44169     /*
44170      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44171      */
44172     preferredTabWidth : 175,
44173     /*
44174      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44175      */
44176     resizeTabs : false,
44177     /*
44178      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44179      */
44180     monitorResize : true,
44181     /*
44182      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44183      */
44184     toolbar : false,  // set by caller..
44185     
44186     region : false, /// set by caller
44187     
44188     disableTooltips : true, // not used yet...
44189
44190     /**
44191      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44192      * @param {String} id The id of the div to use <b>or create</b>
44193      * @param {String} text The text for the tab
44194      * @param {String} content (optional) Content to put in the TabPanelItem body
44195      * @param {Boolean} closable (optional) True to create a close icon on the tab
44196      * @return {Roo.TabPanelItem} The created TabPanelItem
44197      */
44198     addTab : function(id, text, content, closable, tpl)
44199     {
44200         var item = new Roo.bootstrap.panel.TabItem({
44201             panel: this,
44202             id : id,
44203             text : text,
44204             closable : closable,
44205             tpl : tpl
44206         });
44207         this.addTabItem(item);
44208         if(content){
44209             item.setContent(content);
44210         }
44211         return item;
44212     },
44213
44214     /**
44215      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44216      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44217      * @return {Roo.TabPanelItem}
44218      */
44219     getTab : function(id){
44220         return this.items[id];
44221     },
44222
44223     /**
44224      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44225      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44226      */
44227     hideTab : function(id){
44228         var t = this.items[id];
44229         if(!t.isHidden()){
44230            t.setHidden(true);
44231            this.hiddenCount++;
44232            this.autoSizeTabs();
44233         }
44234     },
44235
44236     /**
44237      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44238      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44239      */
44240     unhideTab : function(id){
44241         var t = this.items[id];
44242         if(t.isHidden()){
44243            t.setHidden(false);
44244            this.hiddenCount--;
44245            this.autoSizeTabs();
44246         }
44247     },
44248
44249     /**
44250      * Adds an existing {@link Roo.TabPanelItem}.
44251      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44252      */
44253     addTabItem : function(item)
44254     {
44255         this.items[item.id] = item;
44256         this.items.push(item);
44257         this.autoSizeTabs();
44258       //  if(this.resizeTabs){
44259     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44260   //         this.autoSizeTabs();
44261 //        }else{
44262 //            item.autoSize();
44263        // }
44264     },
44265
44266     /**
44267      * Removes a {@link Roo.TabPanelItem}.
44268      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44269      */
44270     removeTab : function(id){
44271         var items = this.items;
44272         var tab = items[id];
44273         if(!tab) { return; }
44274         var index = items.indexOf(tab);
44275         if(this.active == tab && items.length > 1){
44276             var newTab = this.getNextAvailable(index);
44277             if(newTab) {
44278                 newTab.activate();
44279             }
44280         }
44281         this.stripEl.dom.removeChild(tab.pnode.dom);
44282         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44283             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44284         }
44285         items.splice(index, 1);
44286         delete this.items[tab.id];
44287         tab.fireEvent("close", tab);
44288         tab.purgeListeners();
44289         this.autoSizeTabs();
44290     },
44291
44292     getNextAvailable : function(start){
44293         var items = this.items;
44294         var index = start;
44295         // look for a next tab that will slide over to
44296         // replace the one being removed
44297         while(index < items.length){
44298             var item = items[++index];
44299             if(item && !item.isHidden()){
44300                 return item;
44301             }
44302         }
44303         // if one isn't found select the previous tab (on the left)
44304         index = start;
44305         while(index >= 0){
44306             var item = items[--index];
44307             if(item && !item.isHidden()){
44308                 return item;
44309             }
44310         }
44311         return null;
44312     },
44313
44314     /**
44315      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44316      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44317      */
44318     disableTab : function(id){
44319         var tab = this.items[id];
44320         if(tab && this.active != tab){
44321             tab.disable();
44322         }
44323     },
44324
44325     /**
44326      * Enables a {@link Roo.TabPanelItem} that is disabled.
44327      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44328      */
44329     enableTab : function(id){
44330         var tab = this.items[id];
44331         tab.enable();
44332     },
44333
44334     /**
44335      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44336      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44337      * @return {Roo.TabPanelItem} The TabPanelItem.
44338      */
44339     activate : function(id)
44340     {
44341         //Roo.log('activite:'  + id);
44342         
44343         var tab = this.items[id];
44344         if(!tab){
44345             return null;
44346         }
44347         if(tab == this.active || tab.disabled){
44348             return tab;
44349         }
44350         var e = {};
44351         this.fireEvent("beforetabchange", this, e, tab);
44352         if(e.cancel !== true && !tab.disabled){
44353             if(this.active){
44354                 this.active.hide();
44355             }
44356             this.active = this.items[id];
44357             this.active.show();
44358             this.fireEvent("tabchange", this, this.active);
44359         }
44360         return tab;
44361     },
44362
44363     /**
44364      * Gets the active {@link Roo.TabPanelItem}.
44365      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44366      */
44367     getActiveTab : function(){
44368         return this.active;
44369     },
44370
44371     /**
44372      * Updates the tab body element to fit the height of the container element
44373      * for overflow scrolling
44374      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44375      */
44376     syncHeight : function(targetHeight){
44377         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44378         var bm = this.bodyEl.getMargins();
44379         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44380         this.bodyEl.setHeight(newHeight);
44381         return newHeight;
44382     },
44383
44384     onResize : function(){
44385         if(this.monitorResize){
44386             this.autoSizeTabs();
44387         }
44388     },
44389
44390     /**
44391      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44392      */
44393     beginUpdate : function(){
44394         this.updating = true;
44395     },
44396
44397     /**
44398      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44399      */
44400     endUpdate : function(){
44401         this.updating = false;
44402         this.autoSizeTabs();
44403     },
44404
44405     /**
44406      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44407      */
44408     autoSizeTabs : function()
44409     {
44410         var count = this.items.length;
44411         var vcount = count - this.hiddenCount;
44412         
44413         if (vcount < 2) {
44414             this.stripEl.hide();
44415         } else {
44416             this.stripEl.show();
44417         }
44418         
44419         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44420             return;
44421         }
44422         
44423         
44424         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44425         var availWidth = Math.floor(w / vcount);
44426         var b = this.stripBody;
44427         if(b.getWidth() > w){
44428             var tabs = this.items;
44429             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44430             if(availWidth < this.minTabWidth){
44431                 /*if(!this.sleft){    // incomplete scrolling code
44432                     this.createScrollButtons();
44433                 }
44434                 this.showScroll();
44435                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44436             }
44437         }else{
44438             if(this.currentTabWidth < this.preferredTabWidth){
44439                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44440             }
44441         }
44442     },
44443
44444     /**
44445      * Returns the number of tabs in this TabPanel.
44446      * @return {Number}
44447      */
44448      getCount : function(){
44449          return this.items.length;
44450      },
44451
44452     /**
44453      * Resizes all the tabs to the passed width
44454      * @param {Number} The new width
44455      */
44456     setTabWidth : function(width){
44457         this.currentTabWidth = width;
44458         for(var i = 0, len = this.items.length; i < len; i++) {
44459                 if(!this.items[i].isHidden()) {
44460                 this.items[i].setWidth(width);
44461             }
44462         }
44463     },
44464
44465     /**
44466      * Destroys this TabPanel
44467      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44468      */
44469     destroy : function(removeEl){
44470         Roo.EventManager.removeResizeListener(this.onResize, this);
44471         for(var i = 0, len = this.items.length; i < len; i++){
44472             this.items[i].purgeListeners();
44473         }
44474         if(removeEl === true){
44475             this.el.update("");
44476             this.el.remove();
44477         }
44478     },
44479     
44480     createStrip : function(container)
44481     {
44482         var strip = document.createElement("nav");
44483         strip.className = Roo.bootstrap.version == 4 ?
44484             "navbar-light bg-light" : 
44485             "navbar navbar-default"; //"x-tabs-wrap";
44486         container.appendChild(strip);
44487         return strip;
44488     },
44489     
44490     createStripList : function(strip)
44491     {
44492         // div wrapper for retard IE
44493         // returns the "tr" element.
44494         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44495         //'<div class="x-tabs-strip-wrap">'+
44496           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44497           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44498         return strip.firstChild; //.firstChild.firstChild.firstChild;
44499     },
44500     createBody : function(container)
44501     {
44502         var body = document.createElement("div");
44503         Roo.id(body, "tab-body");
44504         //Roo.fly(body).addClass("x-tabs-body");
44505         Roo.fly(body).addClass("tab-content");
44506         container.appendChild(body);
44507         return body;
44508     },
44509     createItemBody :function(bodyEl, id){
44510         var body = Roo.getDom(id);
44511         if(!body){
44512             body = document.createElement("div");
44513             body.id = id;
44514         }
44515         //Roo.fly(body).addClass("x-tabs-item-body");
44516         Roo.fly(body).addClass("tab-pane");
44517          bodyEl.insertBefore(body, bodyEl.firstChild);
44518         return body;
44519     },
44520     /** @private */
44521     createStripElements :  function(stripEl, text, closable, tpl)
44522     {
44523         var td = document.createElement("li"); // was td..
44524         td.className = 'nav-item';
44525         
44526         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44527         
44528         
44529         stripEl.appendChild(td);
44530         /*if(closable){
44531             td.className = "x-tabs-closable";
44532             if(!this.closeTpl){
44533                 this.closeTpl = new Roo.Template(
44534                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44535                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44536                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44537                 );
44538             }
44539             var el = this.closeTpl.overwrite(td, {"text": text});
44540             var close = el.getElementsByTagName("div")[0];
44541             var inner = el.getElementsByTagName("em")[0];
44542             return {"el": el, "close": close, "inner": inner};
44543         } else {
44544         */
44545         // not sure what this is..
44546 //            if(!this.tabTpl){
44547                 //this.tabTpl = new Roo.Template(
44548                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44549                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44550                 //);
44551 //                this.tabTpl = new Roo.Template(
44552 //                   '<a href="#">' +
44553 //                   '<span unselectable="on"' +
44554 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44555 //                            ' >{text}</span></a>'
44556 //                );
44557 //                
44558 //            }
44559
44560
44561             var template = tpl || this.tabTpl || false;
44562             
44563             if(!template){
44564                 template =  new Roo.Template(
44565                         Roo.bootstrap.version == 4 ? 
44566                             (
44567                                 '<a class="nav-link" href="#" unselectable="on"' +
44568                                      (this.disableTooltips ? '' : ' title="{text}"') +
44569                                      ' >{text}</a>'
44570                             ) : (
44571                                 '<a class="nav-link" href="#">' +
44572                                 '<span unselectable="on"' +
44573                                          (this.disableTooltips ? '' : ' title="{text}"') +
44574                                     ' >{text}</span></a>'
44575                             )
44576                 );
44577             }
44578             
44579             switch (typeof(template)) {
44580                 case 'object' :
44581                     break;
44582                 case 'string' :
44583                     template = new Roo.Template(template);
44584                     break;
44585                 default :
44586                     break;
44587             }
44588             
44589             var el = template.overwrite(td, {"text": text});
44590             
44591             var inner = el.getElementsByTagName("span")[0];
44592             
44593             return {"el": el, "inner": inner};
44594             
44595     }
44596         
44597     
44598 });
44599
44600 /**
44601  * @class Roo.TabPanelItem
44602  * @extends Roo.util.Observable
44603  * Represents an individual item (tab plus body) in a TabPanel.
44604  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44605  * @param {String} id The id of this TabPanelItem
44606  * @param {String} text The text for the tab of this TabPanelItem
44607  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44608  */
44609 Roo.bootstrap.panel.TabItem = function(config){
44610     /**
44611      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44612      * @type Roo.TabPanel
44613      */
44614     this.tabPanel = config.panel;
44615     /**
44616      * The id for this TabPanelItem
44617      * @type String
44618      */
44619     this.id = config.id;
44620     /** @private */
44621     this.disabled = false;
44622     /** @private */
44623     this.text = config.text;
44624     /** @private */
44625     this.loaded = false;
44626     this.closable = config.closable;
44627
44628     /**
44629      * The body element for this TabPanelItem.
44630      * @type Roo.Element
44631      */
44632     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44633     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44634     this.bodyEl.setStyle("display", "block");
44635     this.bodyEl.setStyle("zoom", "1");
44636     //this.hideAction();
44637
44638     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44639     /** @private */
44640     this.el = Roo.get(els.el);
44641     this.inner = Roo.get(els.inner, true);
44642      this.textEl = Roo.bootstrap.version == 4 ?
44643         this.el : Roo.get(this.el.dom.firstChild, true);
44644
44645     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44646     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44647
44648     
44649 //    this.el.on("mousedown", this.onTabMouseDown, this);
44650     this.el.on("click", this.onTabClick, this);
44651     /** @private */
44652     if(config.closable){
44653         var c = Roo.get(els.close, true);
44654         c.dom.title = this.closeText;
44655         c.addClassOnOver("close-over");
44656         c.on("click", this.closeClick, this);
44657      }
44658
44659     this.addEvents({
44660          /**
44661          * @event activate
44662          * Fires when this tab becomes the active tab.
44663          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44664          * @param {Roo.TabPanelItem} this
44665          */
44666         "activate": true,
44667         /**
44668          * @event beforeclose
44669          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44670          * @param {Roo.TabPanelItem} this
44671          * @param {Object} e Set cancel to true on this object to cancel the close.
44672          */
44673         "beforeclose": true,
44674         /**
44675          * @event close
44676          * Fires when this tab is closed.
44677          * @param {Roo.TabPanelItem} this
44678          */
44679          "close": true,
44680         /**
44681          * @event deactivate
44682          * Fires when this tab is no longer the active tab.
44683          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44684          * @param {Roo.TabPanelItem} this
44685          */
44686          "deactivate" : true
44687     });
44688     this.hidden = false;
44689
44690     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44691 };
44692
44693 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44694            {
44695     purgeListeners : function(){
44696        Roo.util.Observable.prototype.purgeListeners.call(this);
44697        this.el.removeAllListeners();
44698     },
44699     /**
44700      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44701      */
44702     show : function(){
44703         this.status_node.addClass("active");
44704         this.showAction();
44705         if(Roo.isOpera){
44706             this.tabPanel.stripWrap.repaint();
44707         }
44708         this.fireEvent("activate", this.tabPanel, this);
44709     },
44710
44711     /**
44712      * Returns true if this tab is the active tab.
44713      * @return {Boolean}
44714      */
44715     isActive : function(){
44716         return this.tabPanel.getActiveTab() == this;
44717     },
44718
44719     /**
44720      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44721      */
44722     hide : function(){
44723         this.status_node.removeClass("active");
44724         this.hideAction();
44725         this.fireEvent("deactivate", this.tabPanel, this);
44726     },
44727
44728     hideAction : function(){
44729         this.bodyEl.hide();
44730         this.bodyEl.setStyle("position", "absolute");
44731         this.bodyEl.setLeft("-20000px");
44732         this.bodyEl.setTop("-20000px");
44733     },
44734
44735     showAction : function(){
44736         this.bodyEl.setStyle("position", "relative");
44737         this.bodyEl.setTop("");
44738         this.bodyEl.setLeft("");
44739         this.bodyEl.show();
44740     },
44741
44742     /**
44743      * Set the tooltip for the tab.
44744      * @param {String} tooltip The tab's tooltip
44745      */
44746     setTooltip : function(text){
44747         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44748             this.textEl.dom.qtip = text;
44749             this.textEl.dom.removeAttribute('title');
44750         }else{
44751             this.textEl.dom.title = text;
44752         }
44753     },
44754
44755     onTabClick : function(e){
44756         e.preventDefault();
44757         this.tabPanel.activate(this.id);
44758     },
44759
44760     onTabMouseDown : function(e){
44761         e.preventDefault();
44762         this.tabPanel.activate(this.id);
44763     },
44764 /*
44765     getWidth : function(){
44766         return this.inner.getWidth();
44767     },
44768
44769     setWidth : function(width){
44770         var iwidth = width - this.linode.getPadding("lr");
44771         this.inner.setWidth(iwidth);
44772         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44773         this.linode.setWidth(width);
44774     },
44775 */
44776     /**
44777      * Show or hide the tab
44778      * @param {Boolean} hidden True to hide or false to show.
44779      */
44780     setHidden : function(hidden){
44781         this.hidden = hidden;
44782         this.linode.setStyle("display", hidden ? "none" : "");
44783     },
44784
44785     /**
44786      * Returns true if this tab is "hidden"
44787      * @return {Boolean}
44788      */
44789     isHidden : function(){
44790         return this.hidden;
44791     },
44792
44793     /**
44794      * Returns the text for this tab
44795      * @return {String}
44796      */
44797     getText : function(){
44798         return this.text;
44799     },
44800     /*
44801     autoSize : function(){
44802         //this.el.beginMeasure();
44803         this.textEl.setWidth(1);
44804         /*
44805          *  #2804 [new] Tabs in Roojs
44806          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44807          */
44808         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44809         //this.el.endMeasure();
44810     //},
44811
44812     /**
44813      * Sets the text for the tab (Note: this also sets the tooltip text)
44814      * @param {String} text The tab's text and tooltip
44815      */
44816     setText : function(text){
44817         this.text = text;
44818         this.textEl.update(text);
44819         this.setTooltip(text);
44820         //if(!this.tabPanel.resizeTabs){
44821         //    this.autoSize();
44822         //}
44823     },
44824     /**
44825      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44826      */
44827     activate : function(){
44828         this.tabPanel.activate(this.id);
44829     },
44830
44831     /**
44832      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44833      */
44834     disable : function(){
44835         if(this.tabPanel.active != this){
44836             this.disabled = true;
44837             this.status_node.addClass("disabled");
44838         }
44839     },
44840
44841     /**
44842      * Enables this TabPanelItem if it was previously disabled.
44843      */
44844     enable : function(){
44845         this.disabled = false;
44846         this.status_node.removeClass("disabled");
44847     },
44848
44849     /**
44850      * Sets the content for this TabPanelItem.
44851      * @param {String} content The content
44852      * @param {Boolean} loadScripts true to look for and load scripts
44853      */
44854     setContent : function(content, loadScripts){
44855         this.bodyEl.update(content, loadScripts);
44856     },
44857
44858     /**
44859      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44860      * @return {Roo.UpdateManager} The UpdateManager
44861      */
44862     getUpdateManager : function(){
44863         return this.bodyEl.getUpdateManager();
44864     },
44865
44866     /**
44867      * Set a URL to be used to load the content for this TabPanelItem.
44868      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44869      * @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)
44870      * @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)
44871      * @return {Roo.UpdateManager} The UpdateManager
44872      */
44873     setUrl : function(url, params, loadOnce){
44874         if(this.refreshDelegate){
44875             this.un('activate', this.refreshDelegate);
44876         }
44877         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44878         this.on("activate", this.refreshDelegate);
44879         return this.bodyEl.getUpdateManager();
44880     },
44881
44882     /** @private */
44883     _handleRefresh : function(url, params, loadOnce){
44884         if(!loadOnce || !this.loaded){
44885             var updater = this.bodyEl.getUpdateManager();
44886             updater.update(url, params, this._setLoaded.createDelegate(this));
44887         }
44888     },
44889
44890     /**
44891      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44892      *   Will fail silently if the setUrl method has not been called.
44893      *   This does not activate the panel, just updates its content.
44894      */
44895     refresh : function(){
44896         if(this.refreshDelegate){
44897            this.loaded = false;
44898            this.refreshDelegate();
44899         }
44900     },
44901
44902     /** @private */
44903     _setLoaded : function(){
44904         this.loaded = true;
44905     },
44906
44907     /** @private */
44908     closeClick : function(e){
44909         var o = {};
44910         e.stopEvent();
44911         this.fireEvent("beforeclose", this, o);
44912         if(o.cancel !== true){
44913             this.tabPanel.removeTab(this.id);
44914         }
44915     },
44916     /**
44917      * The text displayed in the tooltip for the close icon.
44918      * @type String
44919      */
44920     closeText : "Close this tab"
44921 });
44922 /**
44923 *    This script refer to:
44924 *    Title: International Telephone Input
44925 *    Author: Jack O'Connor
44926 *    Code version:  v12.1.12
44927 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44928 **/
44929
44930 Roo.bootstrap.form.PhoneInputData = function() {
44931     var d = [
44932       [
44933         "Afghanistan (‫افغانستان‬‎)",
44934         "af",
44935         "93"
44936       ],
44937       [
44938         "Albania (Shqipëri)",
44939         "al",
44940         "355"
44941       ],
44942       [
44943         "Algeria (‫الجزائر‬‎)",
44944         "dz",
44945         "213"
44946       ],
44947       [
44948         "American Samoa",
44949         "as",
44950         "1684"
44951       ],
44952       [
44953         "Andorra",
44954         "ad",
44955         "376"
44956       ],
44957       [
44958         "Angola",
44959         "ao",
44960         "244"
44961       ],
44962       [
44963         "Anguilla",
44964         "ai",
44965         "1264"
44966       ],
44967       [
44968         "Antigua and Barbuda",
44969         "ag",
44970         "1268"
44971       ],
44972       [
44973         "Argentina",
44974         "ar",
44975         "54"
44976       ],
44977       [
44978         "Armenia (Հայաստան)",
44979         "am",
44980         "374"
44981       ],
44982       [
44983         "Aruba",
44984         "aw",
44985         "297"
44986       ],
44987       [
44988         "Australia",
44989         "au",
44990         "61",
44991         0
44992       ],
44993       [
44994         "Austria (Österreich)",
44995         "at",
44996         "43"
44997       ],
44998       [
44999         "Azerbaijan (Azərbaycan)",
45000         "az",
45001         "994"
45002       ],
45003       [
45004         "Bahamas",
45005         "bs",
45006         "1242"
45007       ],
45008       [
45009         "Bahrain (‫البحرين‬‎)",
45010         "bh",
45011         "973"
45012       ],
45013       [
45014         "Bangladesh (বাংলাদেশ)",
45015         "bd",
45016         "880"
45017       ],
45018       [
45019         "Barbados",
45020         "bb",
45021         "1246"
45022       ],
45023       [
45024         "Belarus (Беларусь)",
45025         "by",
45026         "375"
45027       ],
45028       [
45029         "Belgium (België)",
45030         "be",
45031         "32"
45032       ],
45033       [
45034         "Belize",
45035         "bz",
45036         "501"
45037       ],
45038       [
45039         "Benin (Bénin)",
45040         "bj",
45041         "229"
45042       ],
45043       [
45044         "Bermuda",
45045         "bm",
45046         "1441"
45047       ],
45048       [
45049         "Bhutan (འབྲུག)",
45050         "bt",
45051         "975"
45052       ],
45053       [
45054         "Bolivia",
45055         "bo",
45056         "591"
45057       ],
45058       [
45059         "Bosnia and Herzegovina (Босна и Херцеговина)",
45060         "ba",
45061         "387"
45062       ],
45063       [
45064         "Botswana",
45065         "bw",
45066         "267"
45067       ],
45068       [
45069         "Brazil (Brasil)",
45070         "br",
45071         "55"
45072       ],
45073       [
45074         "British Indian Ocean Territory",
45075         "io",
45076         "246"
45077       ],
45078       [
45079         "British Virgin Islands",
45080         "vg",
45081         "1284"
45082       ],
45083       [
45084         "Brunei",
45085         "bn",
45086         "673"
45087       ],
45088       [
45089         "Bulgaria (България)",
45090         "bg",
45091         "359"
45092       ],
45093       [
45094         "Burkina Faso",
45095         "bf",
45096         "226"
45097       ],
45098       [
45099         "Burundi (Uburundi)",
45100         "bi",
45101         "257"
45102       ],
45103       [
45104         "Cambodia (កម្ពុជា)",
45105         "kh",
45106         "855"
45107       ],
45108       [
45109         "Cameroon (Cameroun)",
45110         "cm",
45111         "237"
45112       ],
45113       [
45114         "Canada",
45115         "ca",
45116         "1",
45117         1,
45118         ["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"]
45119       ],
45120       [
45121         "Cape Verde (Kabu Verdi)",
45122         "cv",
45123         "238"
45124       ],
45125       [
45126         "Caribbean Netherlands",
45127         "bq",
45128         "599",
45129         1
45130       ],
45131       [
45132         "Cayman Islands",
45133         "ky",
45134         "1345"
45135       ],
45136       [
45137         "Central African Republic (République centrafricaine)",
45138         "cf",
45139         "236"
45140       ],
45141       [
45142         "Chad (Tchad)",
45143         "td",
45144         "235"
45145       ],
45146       [
45147         "Chile",
45148         "cl",
45149         "56"
45150       ],
45151       [
45152         "China (中国)",
45153         "cn",
45154         "86"
45155       ],
45156       [
45157         "Christmas Island",
45158         "cx",
45159         "61",
45160         2
45161       ],
45162       [
45163         "Cocos (Keeling) Islands",
45164         "cc",
45165         "61",
45166         1
45167       ],
45168       [
45169         "Colombia",
45170         "co",
45171         "57"
45172       ],
45173       [
45174         "Comoros (‫جزر القمر‬‎)",
45175         "km",
45176         "269"
45177       ],
45178       [
45179         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45180         "cd",
45181         "243"
45182       ],
45183       [
45184         "Congo (Republic) (Congo-Brazzaville)",
45185         "cg",
45186         "242"
45187       ],
45188       [
45189         "Cook Islands",
45190         "ck",
45191         "682"
45192       ],
45193       [
45194         "Costa Rica",
45195         "cr",
45196         "506"
45197       ],
45198       [
45199         "Côte d’Ivoire",
45200         "ci",
45201         "225"
45202       ],
45203       [
45204         "Croatia (Hrvatska)",
45205         "hr",
45206         "385"
45207       ],
45208       [
45209         "Cuba",
45210         "cu",
45211         "53"
45212       ],
45213       [
45214         "Curaçao",
45215         "cw",
45216         "599",
45217         0
45218       ],
45219       [
45220         "Cyprus (Κύπρος)",
45221         "cy",
45222         "357"
45223       ],
45224       [
45225         "Czech Republic (Česká republika)",
45226         "cz",
45227         "420"
45228       ],
45229       [
45230         "Denmark (Danmark)",
45231         "dk",
45232         "45"
45233       ],
45234       [
45235         "Djibouti",
45236         "dj",
45237         "253"
45238       ],
45239       [
45240         "Dominica",
45241         "dm",
45242         "1767"
45243       ],
45244       [
45245         "Dominican Republic (República Dominicana)",
45246         "do",
45247         "1",
45248         2,
45249         ["809", "829", "849"]
45250       ],
45251       [
45252         "Ecuador",
45253         "ec",
45254         "593"
45255       ],
45256       [
45257         "Egypt (‫مصر‬‎)",
45258         "eg",
45259         "20"
45260       ],
45261       [
45262         "El Salvador",
45263         "sv",
45264         "503"
45265       ],
45266       [
45267         "Equatorial Guinea (Guinea Ecuatorial)",
45268         "gq",
45269         "240"
45270       ],
45271       [
45272         "Eritrea",
45273         "er",
45274         "291"
45275       ],
45276       [
45277         "Estonia (Eesti)",
45278         "ee",
45279         "372"
45280       ],
45281       [
45282         "Ethiopia",
45283         "et",
45284         "251"
45285       ],
45286       [
45287         "Falkland Islands (Islas Malvinas)",
45288         "fk",
45289         "500"
45290       ],
45291       [
45292         "Faroe Islands (Føroyar)",
45293         "fo",
45294         "298"
45295       ],
45296       [
45297         "Fiji",
45298         "fj",
45299         "679"
45300       ],
45301       [
45302         "Finland (Suomi)",
45303         "fi",
45304         "358",
45305         0
45306       ],
45307       [
45308         "France",
45309         "fr",
45310         "33"
45311       ],
45312       [
45313         "French Guiana (Guyane française)",
45314         "gf",
45315         "594"
45316       ],
45317       [
45318         "French Polynesia (Polynésie française)",
45319         "pf",
45320         "689"
45321       ],
45322       [
45323         "Gabon",
45324         "ga",
45325         "241"
45326       ],
45327       [
45328         "Gambia",
45329         "gm",
45330         "220"
45331       ],
45332       [
45333         "Georgia (საქართველო)",
45334         "ge",
45335         "995"
45336       ],
45337       [
45338         "Germany (Deutschland)",
45339         "de",
45340         "49"
45341       ],
45342       [
45343         "Ghana (Gaana)",
45344         "gh",
45345         "233"
45346       ],
45347       [
45348         "Gibraltar",
45349         "gi",
45350         "350"
45351       ],
45352       [
45353         "Greece (Ελλάδα)",
45354         "gr",
45355         "30"
45356       ],
45357       [
45358         "Greenland (Kalaallit Nunaat)",
45359         "gl",
45360         "299"
45361       ],
45362       [
45363         "Grenada",
45364         "gd",
45365         "1473"
45366       ],
45367       [
45368         "Guadeloupe",
45369         "gp",
45370         "590",
45371         0
45372       ],
45373       [
45374         "Guam",
45375         "gu",
45376         "1671"
45377       ],
45378       [
45379         "Guatemala",
45380         "gt",
45381         "502"
45382       ],
45383       [
45384         "Guernsey",
45385         "gg",
45386         "44",
45387         1
45388       ],
45389       [
45390         "Guinea (Guinée)",
45391         "gn",
45392         "224"
45393       ],
45394       [
45395         "Guinea-Bissau (Guiné Bissau)",
45396         "gw",
45397         "245"
45398       ],
45399       [
45400         "Guyana",
45401         "gy",
45402         "592"
45403       ],
45404       [
45405         "Haiti",
45406         "ht",
45407         "509"
45408       ],
45409       [
45410         "Honduras",
45411         "hn",
45412         "504"
45413       ],
45414       [
45415         "Hong Kong (香港)",
45416         "hk",
45417         "852"
45418       ],
45419       [
45420         "Hungary (Magyarország)",
45421         "hu",
45422         "36"
45423       ],
45424       [
45425         "Iceland (Ísland)",
45426         "is",
45427         "354"
45428       ],
45429       [
45430         "India (भारत)",
45431         "in",
45432         "91"
45433       ],
45434       [
45435         "Indonesia",
45436         "id",
45437         "62"
45438       ],
45439       [
45440         "Iran (‫ایران‬‎)",
45441         "ir",
45442         "98"
45443       ],
45444       [
45445         "Iraq (‫العراق‬‎)",
45446         "iq",
45447         "964"
45448       ],
45449       [
45450         "Ireland",
45451         "ie",
45452         "353"
45453       ],
45454       [
45455         "Isle of Man",
45456         "im",
45457         "44",
45458         2
45459       ],
45460       [
45461         "Israel (‫ישראל‬‎)",
45462         "il",
45463         "972"
45464       ],
45465       [
45466         "Italy (Italia)",
45467         "it",
45468         "39",
45469         0
45470       ],
45471       [
45472         "Jamaica",
45473         "jm",
45474         "1876"
45475       ],
45476       [
45477         "Japan (日本)",
45478         "jp",
45479         "81"
45480       ],
45481       [
45482         "Jersey",
45483         "je",
45484         "44",
45485         3
45486       ],
45487       [
45488         "Jordan (‫الأردن‬‎)",
45489         "jo",
45490         "962"
45491       ],
45492       [
45493         "Kazakhstan (Казахстан)",
45494         "kz",
45495         "7",
45496         1
45497       ],
45498       [
45499         "Kenya",
45500         "ke",
45501         "254"
45502       ],
45503       [
45504         "Kiribati",
45505         "ki",
45506         "686"
45507       ],
45508       [
45509         "Kosovo",
45510         "xk",
45511         "383"
45512       ],
45513       [
45514         "Kuwait (‫الكويت‬‎)",
45515         "kw",
45516         "965"
45517       ],
45518       [
45519         "Kyrgyzstan (Кыргызстан)",
45520         "kg",
45521         "996"
45522       ],
45523       [
45524         "Laos (ລາວ)",
45525         "la",
45526         "856"
45527       ],
45528       [
45529         "Latvia (Latvija)",
45530         "lv",
45531         "371"
45532       ],
45533       [
45534         "Lebanon (‫لبنان‬‎)",
45535         "lb",
45536         "961"
45537       ],
45538       [
45539         "Lesotho",
45540         "ls",
45541         "266"
45542       ],
45543       [
45544         "Liberia",
45545         "lr",
45546         "231"
45547       ],
45548       [
45549         "Libya (‫ليبيا‬‎)",
45550         "ly",
45551         "218"
45552       ],
45553       [
45554         "Liechtenstein",
45555         "li",
45556         "423"
45557       ],
45558       [
45559         "Lithuania (Lietuva)",
45560         "lt",
45561         "370"
45562       ],
45563       [
45564         "Luxembourg",
45565         "lu",
45566         "352"
45567       ],
45568       [
45569         "Macau (澳門)",
45570         "mo",
45571         "853"
45572       ],
45573       [
45574         "Macedonia (FYROM) (Македонија)",
45575         "mk",
45576         "389"
45577       ],
45578       [
45579         "Madagascar (Madagasikara)",
45580         "mg",
45581         "261"
45582       ],
45583       [
45584         "Malawi",
45585         "mw",
45586         "265"
45587       ],
45588       [
45589         "Malaysia",
45590         "my",
45591         "60"
45592       ],
45593       [
45594         "Maldives",
45595         "mv",
45596         "960"
45597       ],
45598       [
45599         "Mali",
45600         "ml",
45601         "223"
45602       ],
45603       [
45604         "Malta",
45605         "mt",
45606         "356"
45607       ],
45608       [
45609         "Marshall Islands",
45610         "mh",
45611         "692"
45612       ],
45613       [
45614         "Martinique",
45615         "mq",
45616         "596"
45617       ],
45618       [
45619         "Mauritania (‫موريتانيا‬‎)",
45620         "mr",
45621         "222"
45622       ],
45623       [
45624         "Mauritius (Moris)",
45625         "mu",
45626         "230"
45627       ],
45628       [
45629         "Mayotte",
45630         "yt",
45631         "262",
45632         1
45633       ],
45634       [
45635         "Mexico (México)",
45636         "mx",
45637         "52"
45638       ],
45639       [
45640         "Micronesia",
45641         "fm",
45642         "691"
45643       ],
45644       [
45645         "Moldova (Republica Moldova)",
45646         "md",
45647         "373"
45648       ],
45649       [
45650         "Monaco",
45651         "mc",
45652         "377"
45653       ],
45654       [
45655         "Mongolia (Монгол)",
45656         "mn",
45657         "976"
45658       ],
45659       [
45660         "Montenegro (Crna Gora)",
45661         "me",
45662         "382"
45663       ],
45664       [
45665         "Montserrat",
45666         "ms",
45667         "1664"
45668       ],
45669       [
45670         "Morocco (‫المغرب‬‎)",
45671         "ma",
45672         "212",
45673         0
45674       ],
45675       [
45676         "Mozambique (Moçambique)",
45677         "mz",
45678         "258"
45679       ],
45680       [
45681         "Myanmar (Burma) (မြန်မာ)",
45682         "mm",
45683         "95"
45684       ],
45685       [
45686         "Namibia (Namibië)",
45687         "na",
45688         "264"
45689       ],
45690       [
45691         "Nauru",
45692         "nr",
45693         "674"
45694       ],
45695       [
45696         "Nepal (नेपाल)",
45697         "np",
45698         "977"
45699       ],
45700       [
45701         "Netherlands (Nederland)",
45702         "nl",
45703         "31"
45704       ],
45705       [
45706         "New Caledonia (Nouvelle-Calédonie)",
45707         "nc",
45708         "687"
45709       ],
45710       [
45711         "New Zealand",
45712         "nz",
45713         "64"
45714       ],
45715       [
45716         "Nicaragua",
45717         "ni",
45718         "505"
45719       ],
45720       [
45721         "Niger (Nijar)",
45722         "ne",
45723         "227"
45724       ],
45725       [
45726         "Nigeria",
45727         "ng",
45728         "234"
45729       ],
45730       [
45731         "Niue",
45732         "nu",
45733         "683"
45734       ],
45735       [
45736         "Norfolk Island",
45737         "nf",
45738         "672"
45739       ],
45740       [
45741         "North Korea (조선 민주주의 인민 공화국)",
45742         "kp",
45743         "850"
45744       ],
45745       [
45746         "Northern Mariana Islands",
45747         "mp",
45748         "1670"
45749       ],
45750       [
45751         "Norway (Norge)",
45752         "no",
45753         "47",
45754         0
45755       ],
45756       [
45757         "Oman (‫عُمان‬‎)",
45758         "om",
45759         "968"
45760       ],
45761       [
45762         "Pakistan (‫پاکستان‬‎)",
45763         "pk",
45764         "92"
45765       ],
45766       [
45767         "Palau",
45768         "pw",
45769         "680"
45770       ],
45771       [
45772         "Palestine (‫فلسطين‬‎)",
45773         "ps",
45774         "970"
45775       ],
45776       [
45777         "Panama (Panamá)",
45778         "pa",
45779         "507"
45780       ],
45781       [
45782         "Papua New Guinea",
45783         "pg",
45784         "675"
45785       ],
45786       [
45787         "Paraguay",
45788         "py",
45789         "595"
45790       ],
45791       [
45792         "Peru (Perú)",
45793         "pe",
45794         "51"
45795       ],
45796       [
45797         "Philippines",
45798         "ph",
45799         "63"
45800       ],
45801       [
45802         "Poland (Polska)",
45803         "pl",
45804         "48"
45805       ],
45806       [
45807         "Portugal",
45808         "pt",
45809         "351"
45810       ],
45811       [
45812         "Puerto Rico",
45813         "pr",
45814         "1",
45815         3,
45816         ["787", "939"]
45817       ],
45818       [
45819         "Qatar (‫قطر‬‎)",
45820         "qa",
45821         "974"
45822       ],
45823       [
45824         "Réunion (La Réunion)",
45825         "re",
45826         "262",
45827         0
45828       ],
45829       [
45830         "Romania (România)",
45831         "ro",
45832         "40"
45833       ],
45834       [
45835         "Russia (Россия)",
45836         "ru",
45837         "7",
45838         0
45839       ],
45840       [
45841         "Rwanda",
45842         "rw",
45843         "250"
45844       ],
45845       [
45846         "Saint Barthélemy",
45847         "bl",
45848         "590",
45849         1
45850       ],
45851       [
45852         "Saint Helena",
45853         "sh",
45854         "290"
45855       ],
45856       [
45857         "Saint Kitts and Nevis",
45858         "kn",
45859         "1869"
45860       ],
45861       [
45862         "Saint Lucia",
45863         "lc",
45864         "1758"
45865       ],
45866       [
45867         "Saint Martin (Saint-Martin (partie française))",
45868         "mf",
45869         "590",
45870         2
45871       ],
45872       [
45873         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45874         "pm",
45875         "508"
45876       ],
45877       [
45878         "Saint Vincent and the Grenadines",
45879         "vc",
45880         "1784"
45881       ],
45882       [
45883         "Samoa",
45884         "ws",
45885         "685"
45886       ],
45887       [
45888         "San Marino",
45889         "sm",
45890         "378"
45891       ],
45892       [
45893         "São Tomé and Príncipe (São Tomé e Príncipe)",
45894         "st",
45895         "239"
45896       ],
45897       [
45898         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45899         "sa",
45900         "966"
45901       ],
45902       [
45903         "Senegal (Sénégal)",
45904         "sn",
45905         "221"
45906       ],
45907       [
45908         "Serbia (Србија)",
45909         "rs",
45910         "381"
45911       ],
45912       [
45913         "Seychelles",
45914         "sc",
45915         "248"
45916       ],
45917       [
45918         "Sierra Leone",
45919         "sl",
45920         "232"
45921       ],
45922       [
45923         "Singapore",
45924         "sg",
45925         "65"
45926       ],
45927       [
45928         "Sint Maarten",
45929         "sx",
45930         "1721"
45931       ],
45932       [
45933         "Slovakia (Slovensko)",
45934         "sk",
45935         "421"
45936       ],
45937       [
45938         "Slovenia (Slovenija)",
45939         "si",
45940         "386"
45941       ],
45942       [
45943         "Solomon Islands",
45944         "sb",
45945         "677"
45946       ],
45947       [
45948         "Somalia (Soomaaliya)",
45949         "so",
45950         "252"
45951       ],
45952       [
45953         "South Africa",
45954         "za",
45955         "27"
45956       ],
45957       [
45958         "South Korea (대한민국)",
45959         "kr",
45960         "82"
45961       ],
45962       [
45963         "South Sudan (‫جنوب السودان‬‎)",
45964         "ss",
45965         "211"
45966       ],
45967       [
45968         "Spain (España)",
45969         "es",
45970         "34"
45971       ],
45972       [
45973         "Sri Lanka (ශ්‍රී ලංකාව)",
45974         "lk",
45975         "94"
45976       ],
45977       [
45978         "Sudan (‫السودان‬‎)",
45979         "sd",
45980         "249"
45981       ],
45982       [
45983         "Suriname",
45984         "sr",
45985         "597"
45986       ],
45987       [
45988         "Svalbard and Jan Mayen",
45989         "sj",
45990         "47",
45991         1
45992       ],
45993       [
45994         "Swaziland",
45995         "sz",
45996         "268"
45997       ],
45998       [
45999         "Sweden (Sverige)",
46000         "se",
46001         "46"
46002       ],
46003       [
46004         "Switzerland (Schweiz)",
46005         "ch",
46006         "41"
46007       ],
46008       [
46009         "Syria (‫سوريا‬‎)",
46010         "sy",
46011         "963"
46012       ],
46013       [
46014         "Taiwan (台灣)",
46015         "tw",
46016         "886"
46017       ],
46018       [
46019         "Tajikistan",
46020         "tj",
46021         "992"
46022       ],
46023       [
46024         "Tanzania",
46025         "tz",
46026         "255"
46027       ],
46028       [
46029         "Thailand (ไทย)",
46030         "th",
46031         "66"
46032       ],
46033       [
46034         "Timor-Leste",
46035         "tl",
46036         "670"
46037       ],
46038       [
46039         "Togo",
46040         "tg",
46041         "228"
46042       ],
46043       [
46044         "Tokelau",
46045         "tk",
46046         "690"
46047       ],
46048       [
46049         "Tonga",
46050         "to",
46051         "676"
46052       ],
46053       [
46054         "Trinidad and Tobago",
46055         "tt",
46056         "1868"
46057       ],
46058       [
46059         "Tunisia (‫تونس‬‎)",
46060         "tn",
46061         "216"
46062       ],
46063       [
46064         "Turkey (Türkiye)",
46065         "tr",
46066         "90"
46067       ],
46068       [
46069         "Turkmenistan",
46070         "tm",
46071         "993"
46072       ],
46073       [
46074         "Turks and Caicos Islands",
46075         "tc",
46076         "1649"
46077       ],
46078       [
46079         "Tuvalu",
46080         "tv",
46081         "688"
46082       ],
46083       [
46084         "U.S. Virgin Islands",
46085         "vi",
46086         "1340"
46087       ],
46088       [
46089         "Uganda",
46090         "ug",
46091         "256"
46092       ],
46093       [
46094         "Ukraine (Україна)",
46095         "ua",
46096         "380"
46097       ],
46098       [
46099         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46100         "ae",
46101         "971"
46102       ],
46103       [
46104         "United Kingdom",
46105         "gb",
46106         "44",
46107         0
46108       ],
46109       [
46110         "United States",
46111         "us",
46112         "1",
46113         0
46114       ],
46115       [
46116         "Uruguay",
46117         "uy",
46118         "598"
46119       ],
46120       [
46121         "Uzbekistan (Oʻzbekiston)",
46122         "uz",
46123         "998"
46124       ],
46125       [
46126         "Vanuatu",
46127         "vu",
46128         "678"
46129       ],
46130       [
46131         "Vatican City (Città del Vaticano)",
46132         "va",
46133         "39",
46134         1
46135       ],
46136       [
46137         "Venezuela",
46138         "ve",
46139         "58"
46140       ],
46141       [
46142         "Vietnam (Việt Nam)",
46143         "vn",
46144         "84"
46145       ],
46146       [
46147         "Wallis and Futuna (Wallis-et-Futuna)",
46148         "wf",
46149         "681"
46150       ],
46151       [
46152         "Western Sahara (‫الصحراء الغربية‬‎)",
46153         "eh",
46154         "212",
46155         1
46156       ],
46157       [
46158         "Yemen (‫اليمن‬‎)",
46159         "ye",
46160         "967"
46161       ],
46162       [
46163         "Zambia",
46164         "zm",
46165         "260"
46166       ],
46167       [
46168         "Zimbabwe",
46169         "zw",
46170         "263"
46171       ],
46172       [
46173         "Åland Islands",
46174         "ax",
46175         "358",
46176         1
46177       ]
46178   ];
46179   
46180   return d;
46181 }/**
46182 *    This script refer to:
46183 *    Title: International Telephone Input
46184 *    Author: Jack O'Connor
46185 *    Code version:  v12.1.12
46186 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46187 **/
46188
46189 /**
46190  * @class Roo.bootstrap.form.PhoneInput
46191  * @extends Roo.bootstrap.form.TriggerField
46192  * An input with International dial-code selection
46193  
46194  * @cfg {String} defaultDialCode default '+852'
46195  * @cfg {Array} preferedCountries default []
46196   
46197  * @constructor
46198  * Create a new PhoneInput.
46199  * @param {Object} config Configuration options
46200  */
46201
46202 Roo.bootstrap.form.PhoneInput = function(config) {
46203     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46204 };
46205
46206 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46207         /**
46208         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46209         */
46210         listWidth: undefined,
46211         
46212         selectedClass: 'active',
46213         
46214         invalidClass : "has-warning",
46215         
46216         validClass: 'has-success',
46217         
46218         allowed: '0123456789',
46219         
46220         max_length: 15,
46221         
46222         /**
46223          * @cfg {String} defaultDialCode The default dial code when initializing the input
46224          */
46225         defaultDialCode: '+852',
46226         
46227         /**
46228          * @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
46229          */
46230         preferedCountries: false,
46231         
46232         getAutoCreate : function()
46233         {
46234             var data = Roo.bootstrap.form.PhoneInputData();
46235             var align = this.labelAlign || this.parentLabelAlign();
46236             var id = Roo.id();
46237             
46238             this.allCountries = [];
46239             this.dialCodeMapping = [];
46240             
46241             for (var i = 0; i < data.length; i++) {
46242               var c = data[i];
46243               this.allCountries[i] = {
46244                 name: c[0],
46245                 iso2: c[1],
46246                 dialCode: c[2],
46247                 priority: c[3] || 0,
46248                 areaCodes: c[4] || null
46249               };
46250               this.dialCodeMapping[c[2]] = {
46251                   name: c[0],
46252                   iso2: c[1],
46253                   priority: c[3] || 0,
46254                   areaCodes: c[4] || null
46255               };
46256             }
46257             
46258             var cfg = {
46259                 cls: 'form-group',
46260                 cn: []
46261             };
46262             
46263             var input =  {
46264                 tag: 'input',
46265                 id : id,
46266                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46267                 maxlength: this.max_length,
46268                 cls : 'form-control tel-input',
46269                 autocomplete: 'new-password'
46270             };
46271             
46272             var hiddenInput = {
46273                 tag: 'input',
46274                 type: 'hidden',
46275                 cls: 'hidden-tel-input'
46276             };
46277             
46278             if (this.name) {
46279                 hiddenInput.name = this.name;
46280             }
46281             
46282             if (this.disabled) {
46283                 input.disabled = true;
46284             }
46285             
46286             var flag_container = {
46287                 tag: 'div',
46288                 cls: 'flag-box',
46289                 cn: [
46290                     {
46291                         tag: 'div',
46292                         cls: 'flag'
46293                     },
46294                     {
46295                         tag: 'div',
46296                         cls: 'caret'
46297                     }
46298                 ]
46299             };
46300             
46301             var box = {
46302                 tag: 'div',
46303                 cls: this.hasFeedback ? 'has-feedback' : '',
46304                 cn: [
46305                     hiddenInput,
46306                     input,
46307                     {
46308                         tag: 'input',
46309                         cls: 'dial-code-holder',
46310                         disabled: true
46311                     }
46312                 ]
46313             };
46314             
46315             var container = {
46316                 cls: 'roo-select2-container input-group',
46317                 cn: [
46318                     flag_container,
46319                     box
46320                 ]
46321             };
46322             
46323             if (this.fieldLabel.length) {
46324                 var indicator = {
46325                     tag: 'i',
46326                     tooltip: 'This field is required'
46327                 };
46328                 
46329                 var label = {
46330                     tag: 'label',
46331                     'for':  id,
46332                     cls: 'control-label',
46333                     cn: []
46334                 };
46335                 
46336                 var label_text = {
46337                     tag: 'span',
46338                     html: this.fieldLabel
46339                 };
46340                 
46341                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46342                 label.cn = [
46343                     indicator,
46344                     label_text
46345                 ];
46346                 
46347                 if(this.indicatorpos == 'right') {
46348                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46349                     label.cn = [
46350                         label_text,
46351                         indicator
46352                     ];
46353                 }
46354                 
46355                 if(align == 'left') {
46356                     container = {
46357                         tag: 'div',
46358                         cn: [
46359                             container
46360                         ]
46361                     };
46362                     
46363                     if(this.labelWidth > 12){
46364                         label.style = "width: " + this.labelWidth + 'px';
46365                     }
46366                     if(this.labelWidth < 13 && this.labelmd == 0){
46367                         this.labelmd = this.labelWidth;
46368                     }
46369                     if(this.labellg > 0){
46370                         label.cls += ' col-lg-' + this.labellg;
46371                         input.cls += ' col-lg-' + (12 - this.labellg);
46372                     }
46373                     if(this.labelmd > 0){
46374                         label.cls += ' col-md-' + this.labelmd;
46375                         container.cls += ' col-md-' + (12 - this.labelmd);
46376                     }
46377                     if(this.labelsm > 0){
46378                         label.cls += ' col-sm-' + this.labelsm;
46379                         container.cls += ' col-sm-' + (12 - this.labelsm);
46380                     }
46381                     if(this.labelxs > 0){
46382                         label.cls += ' col-xs-' + this.labelxs;
46383                         container.cls += ' col-xs-' + (12 - this.labelxs);
46384                     }
46385                 }
46386             }
46387             
46388             cfg.cn = [
46389                 label,
46390                 container
46391             ];
46392             
46393             var settings = this;
46394             
46395             ['xs','sm','md','lg'].map(function(size){
46396                 if (settings[size]) {
46397                     cfg.cls += ' col-' + size + '-' + settings[size];
46398                 }
46399             });
46400             
46401             this.store = new Roo.data.Store({
46402                 proxy : new Roo.data.MemoryProxy({}),
46403                 reader : new Roo.data.JsonReader({
46404                     fields : [
46405                         {
46406                             'name' : 'name',
46407                             'type' : 'string'
46408                         },
46409                         {
46410                             'name' : 'iso2',
46411                             'type' : 'string'
46412                         },
46413                         {
46414                             'name' : 'dialCode',
46415                             'type' : 'string'
46416                         },
46417                         {
46418                             'name' : 'priority',
46419                             'type' : 'string'
46420                         },
46421                         {
46422                             'name' : 'areaCodes',
46423                             'type' : 'string'
46424                         }
46425                     ]
46426                 })
46427             });
46428             
46429             if(!this.preferedCountries) {
46430                 this.preferedCountries = [
46431                     'hk',
46432                     'gb',
46433                     'us'
46434                 ];
46435             }
46436             
46437             var p = this.preferedCountries.reverse();
46438             
46439             if(p) {
46440                 for (var i = 0; i < p.length; i++) {
46441                     for (var j = 0; j < this.allCountries.length; j++) {
46442                         if(this.allCountries[j].iso2 == p[i]) {
46443                             var t = this.allCountries[j];
46444                             this.allCountries.splice(j,1);
46445                             this.allCountries.unshift(t);
46446                         }
46447                     } 
46448                 }
46449             }
46450             
46451             this.store.proxy.data = {
46452                 success: true,
46453                 data: this.allCountries
46454             };
46455             
46456             return cfg;
46457         },
46458         
46459         initEvents : function()
46460         {
46461             this.createList();
46462             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46463             
46464             this.indicator = this.indicatorEl();
46465             this.flag = this.flagEl();
46466             this.dialCodeHolder = this.dialCodeHolderEl();
46467             
46468             this.trigger = this.el.select('div.flag-box',true).first();
46469             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46470             
46471             var _this = this;
46472             
46473             (function(){
46474                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46475                 _this.list.setWidth(lw);
46476             }).defer(100);
46477             
46478             this.list.on('mouseover', this.onViewOver, this);
46479             this.list.on('mousemove', this.onViewMove, this);
46480             this.inputEl().on("keyup", this.onKeyUp, this);
46481             this.inputEl().on("keypress", this.onKeyPress, this);
46482             
46483             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46484
46485             this.view = new Roo.View(this.list, this.tpl, {
46486                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46487             });
46488             
46489             this.view.on('click', this.onViewClick, this);
46490             this.setValue(this.defaultDialCode);
46491         },
46492         
46493         onTriggerClick : function(e)
46494         {
46495             Roo.log('trigger click');
46496             if(this.disabled){
46497                 return;
46498             }
46499             
46500             if(this.isExpanded()){
46501                 this.collapse();
46502                 this.hasFocus = false;
46503             }else {
46504                 this.store.load({});
46505                 this.hasFocus = true;
46506                 this.expand();
46507             }
46508         },
46509         
46510         isExpanded : function()
46511         {
46512             return this.list.isVisible();
46513         },
46514         
46515         collapse : function()
46516         {
46517             if(!this.isExpanded()){
46518                 return;
46519             }
46520             this.list.hide();
46521             Roo.get(document).un('mousedown', this.collapseIf, this);
46522             Roo.get(document).un('mousewheel', this.collapseIf, this);
46523             this.fireEvent('collapse', this);
46524             this.validate();
46525         },
46526         
46527         expand : function()
46528         {
46529             Roo.log('expand');
46530
46531             if(this.isExpanded() || !this.hasFocus){
46532                 return;
46533             }
46534             
46535             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46536             this.list.setWidth(lw);
46537             
46538             this.list.show();
46539             this.restrictHeight();
46540             
46541             Roo.get(document).on('mousedown', this.collapseIf, this);
46542             Roo.get(document).on('mousewheel', this.collapseIf, this);
46543             
46544             this.fireEvent('expand', this);
46545         },
46546         
46547         restrictHeight : function()
46548         {
46549             this.list.alignTo(this.inputEl(), this.listAlign);
46550             this.list.alignTo(this.inputEl(), this.listAlign);
46551         },
46552         
46553         onViewOver : function(e, t)
46554         {
46555             if(this.inKeyMode){
46556                 return;
46557             }
46558             var item = this.view.findItemFromChild(t);
46559             
46560             if(item){
46561                 var index = this.view.indexOf(item);
46562                 this.select(index, false);
46563             }
46564         },
46565
46566         // private
46567         onViewClick : function(view, doFocus, el, e)
46568         {
46569             var index = this.view.getSelectedIndexes()[0];
46570             
46571             var r = this.store.getAt(index);
46572             
46573             if(r){
46574                 this.onSelect(r, index);
46575             }
46576             if(doFocus !== false && !this.blockFocus){
46577                 this.inputEl().focus();
46578             }
46579         },
46580         
46581         onViewMove : function(e, t)
46582         {
46583             this.inKeyMode = false;
46584         },
46585         
46586         select : function(index, scrollIntoView)
46587         {
46588             this.selectedIndex = index;
46589             this.view.select(index);
46590             if(scrollIntoView !== false){
46591                 var el = this.view.getNode(index);
46592                 if(el){
46593                     this.list.scrollChildIntoView(el, false);
46594                 }
46595             }
46596         },
46597         
46598         createList : function()
46599         {
46600             this.list = Roo.get(document.body).createChild({
46601                 tag: 'ul',
46602                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46603                 style: 'display:none'
46604             });
46605             
46606             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46607         },
46608         
46609         collapseIf : function(e)
46610         {
46611             var in_combo  = e.within(this.el);
46612             var in_list =  e.within(this.list);
46613             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46614             
46615             if (in_combo || in_list || is_list) {
46616                 return;
46617             }
46618             this.collapse();
46619         },
46620         
46621         onSelect : function(record, index)
46622         {
46623             if(this.fireEvent('beforeselect', this, record, index) !== false){
46624                 
46625                 this.setFlagClass(record.data.iso2);
46626                 this.setDialCode(record.data.dialCode);
46627                 this.hasFocus = false;
46628                 this.collapse();
46629                 this.fireEvent('select', this, record, index);
46630             }
46631         },
46632         
46633         flagEl : function()
46634         {
46635             var flag = this.el.select('div.flag',true).first();
46636             if(!flag){
46637                 return false;
46638             }
46639             return flag;
46640         },
46641         
46642         dialCodeHolderEl : function()
46643         {
46644             var d = this.el.select('input.dial-code-holder',true).first();
46645             if(!d){
46646                 return false;
46647             }
46648             return d;
46649         },
46650         
46651         setDialCode : function(v)
46652         {
46653             this.dialCodeHolder.dom.value = '+'+v;
46654         },
46655         
46656         setFlagClass : function(n)
46657         {
46658             this.flag.dom.className = 'flag '+n;
46659         },
46660         
46661         getValue : function()
46662         {
46663             var v = this.inputEl().getValue();
46664             if(this.dialCodeHolder) {
46665                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46666             }
46667             return v;
46668         },
46669         
46670         setValue : function(v)
46671         {
46672             var d = this.getDialCode(v);
46673             
46674             //invalid dial code
46675             if(v.length == 0 || !d || d.length == 0) {
46676                 if(this.rendered){
46677                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46678                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46679                 }
46680                 return;
46681             }
46682             
46683             //valid dial code
46684             this.setFlagClass(this.dialCodeMapping[d].iso2);
46685             this.setDialCode(d);
46686             this.inputEl().dom.value = v.replace('+'+d,'');
46687             this.hiddenEl().dom.value = this.getValue();
46688             
46689             this.validate();
46690         },
46691         
46692         getDialCode : function(v)
46693         {
46694             v = v ||  '';
46695             
46696             if (v.length == 0) {
46697                 return this.dialCodeHolder.dom.value;
46698             }
46699             
46700             var dialCode = "";
46701             if (v.charAt(0) != "+") {
46702                 return false;
46703             }
46704             var numericChars = "";
46705             for (var i = 1; i < v.length; i++) {
46706               var c = v.charAt(i);
46707               if (!isNaN(c)) {
46708                 numericChars += c;
46709                 if (this.dialCodeMapping[numericChars]) {
46710                   dialCode = v.substr(1, i);
46711                 }
46712                 if (numericChars.length == 4) {
46713                   break;
46714                 }
46715               }
46716             }
46717             return dialCode;
46718         },
46719         
46720         reset : function()
46721         {
46722             this.setValue(this.defaultDialCode);
46723             this.validate();
46724         },
46725         
46726         hiddenEl : function()
46727         {
46728             return this.el.select('input.hidden-tel-input',true).first();
46729         },
46730         
46731         // after setting val
46732         onKeyUp : function(e){
46733             this.setValue(this.getValue());
46734         },
46735         
46736         onKeyPress : function(e){
46737             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46738                 e.stopEvent();
46739             }
46740         }
46741         
46742 });
46743 /**
46744  * @class Roo.bootstrap.form.MoneyField
46745  * @extends Roo.bootstrap.form.ComboBox
46746  * Bootstrap MoneyField class
46747  * 
46748  * @constructor
46749  * Create a new MoneyField.
46750  * @param {Object} config Configuration options
46751  */
46752
46753 Roo.bootstrap.form.MoneyField = function(config) {
46754     
46755     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46756     
46757 };
46758
46759 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46760     
46761     /**
46762      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46763      */
46764     allowDecimals : true,
46765     /**
46766      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46767      */
46768     decimalSeparator : ".",
46769     /**
46770      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46771      */
46772     decimalPrecision : 0,
46773     /**
46774      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46775      */
46776     allowNegative : true,
46777     /**
46778      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46779      */
46780     allowZero: true,
46781     /**
46782      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46783      */
46784     minValue : Number.NEGATIVE_INFINITY,
46785     /**
46786      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46787      */
46788     maxValue : Number.MAX_VALUE,
46789     /**
46790      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46791      */
46792     minText : "The minimum value for this field is {0}",
46793     /**
46794      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46795      */
46796     maxText : "The maximum value for this field is {0}",
46797     /**
46798      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46799      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46800      */
46801     nanText : "{0} is not a valid number",
46802     /**
46803      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46804      */
46805     castInt : true,
46806     /**
46807      * @cfg {String} defaults currency of the MoneyField
46808      * value should be in lkey
46809      */
46810     defaultCurrency : false,
46811     /**
46812      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46813      */
46814     thousandsDelimiter : false,
46815     /**
46816      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46817      */
46818     max_length: false,
46819     
46820     inputlg : 9,
46821     inputmd : 9,
46822     inputsm : 9,
46823     inputxs : 6,
46824      /**
46825      * @cfg {Roo.data.Store} store  Store to lookup currency??
46826      */
46827     store : false,
46828     
46829     getAutoCreate : function()
46830     {
46831         var align = this.labelAlign || this.parentLabelAlign();
46832         
46833         var id = Roo.id();
46834
46835         var cfg = {
46836             cls: 'form-group',
46837             cn: []
46838         };
46839
46840         var input =  {
46841             tag: 'input',
46842             id : id,
46843             cls : 'form-control roo-money-amount-input',
46844             autocomplete: 'new-password'
46845         };
46846         
46847         var hiddenInput = {
46848             tag: 'input',
46849             type: 'hidden',
46850             id: Roo.id(),
46851             cls: 'hidden-number-input'
46852         };
46853         
46854         if(this.max_length) {
46855             input.maxlength = this.max_length; 
46856         }
46857         
46858         if (this.name) {
46859             hiddenInput.name = this.name;
46860         }
46861
46862         if (this.disabled) {
46863             input.disabled = true;
46864         }
46865
46866         var clg = 12 - this.inputlg;
46867         var cmd = 12 - this.inputmd;
46868         var csm = 12 - this.inputsm;
46869         var cxs = 12 - this.inputxs;
46870         
46871         var container = {
46872             tag : 'div',
46873             cls : 'row roo-money-field',
46874             cn : [
46875                 {
46876                     tag : 'div',
46877                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46878                     cn : [
46879                         {
46880                             tag : 'div',
46881                             cls: 'roo-select2-container input-group',
46882                             cn: [
46883                                 {
46884                                     tag : 'input',
46885                                     cls : 'form-control roo-money-currency-input',
46886                                     autocomplete: 'new-password',
46887                                     readOnly : 1,
46888                                     name : this.currencyName
46889                                 },
46890                                 {
46891                                     tag :'span',
46892                                     cls : 'input-group-addon',
46893                                     cn : [
46894                                         {
46895                                             tag: 'span',
46896                                             cls: 'caret'
46897                                         }
46898                                     ]
46899                                 }
46900                             ]
46901                         }
46902                     ]
46903                 },
46904                 {
46905                     tag : 'div',
46906                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46907                     cn : [
46908                         {
46909                             tag: 'div',
46910                             cls: this.hasFeedback ? 'has-feedback' : '',
46911                             cn: [
46912                                 input
46913                             ]
46914                         }
46915                     ]
46916                 }
46917             ]
46918             
46919         };
46920         
46921         if (this.fieldLabel.length) {
46922             var indicator = {
46923                 tag: 'i',
46924                 tooltip: 'This field is required'
46925             };
46926
46927             var label = {
46928                 tag: 'label',
46929                 'for':  id,
46930                 cls: 'control-label',
46931                 cn: []
46932             };
46933
46934             var label_text = {
46935                 tag: 'span',
46936                 html: this.fieldLabel
46937             };
46938
46939             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46940             label.cn = [
46941                 indicator,
46942                 label_text
46943             ];
46944
46945             if(this.indicatorpos == 'right') {
46946                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46947                 label.cn = [
46948                     label_text,
46949                     indicator
46950                 ];
46951             }
46952
46953             if(align == 'left') {
46954                 container = {
46955                     tag: 'div',
46956                     cn: [
46957                         container
46958                     ]
46959                 };
46960
46961                 if(this.labelWidth > 12){
46962                     label.style = "width: " + this.labelWidth + 'px';
46963                 }
46964                 if(this.labelWidth < 13 && this.labelmd == 0){
46965                     this.labelmd = this.labelWidth;
46966                 }
46967                 if(this.labellg > 0){
46968                     label.cls += ' col-lg-' + this.labellg;
46969                     input.cls += ' col-lg-' + (12 - this.labellg);
46970                 }
46971                 if(this.labelmd > 0){
46972                     label.cls += ' col-md-' + this.labelmd;
46973                     container.cls += ' col-md-' + (12 - this.labelmd);
46974                 }
46975                 if(this.labelsm > 0){
46976                     label.cls += ' col-sm-' + this.labelsm;
46977                     container.cls += ' col-sm-' + (12 - this.labelsm);
46978                 }
46979                 if(this.labelxs > 0){
46980                     label.cls += ' col-xs-' + this.labelxs;
46981                     container.cls += ' col-xs-' + (12 - this.labelxs);
46982                 }
46983             }
46984         }
46985
46986         cfg.cn = [
46987             label,
46988             container,
46989             hiddenInput
46990         ];
46991         
46992         var settings = this;
46993
46994         ['xs','sm','md','lg'].map(function(size){
46995             if (settings[size]) {
46996                 cfg.cls += ' col-' + size + '-' + settings[size];
46997             }
46998         });
46999         
47000         return cfg;
47001     },
47002     
47003     initEvents : function()
47004     {
47005         this.indicator = this.indicatorEl();
47006         
47007         this.initCurrencyEvent();
47008         
47009         this.initNumberEvent();
47010     },
47011     
47012     initCurrencyEvent : function()
47013     {
47014         if (!this.store) {
47015             throw "can not find store for combo";
47016         }
47017         
47018         this.store = Roo.factory(this.store, Roo.data);
47019         this.store.parent = this;
47020         
47021         this.createList();
47022         
47023         this.triggerEl = this.el.select('.input-group-addon', true).first();
47024         
47025         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47026         
47027         var _this = this;
47028         
47029         (function(){
47030             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47031             _this.list.setWidth(lw);
47032         }).defer(100);
47033         
47034         this.list.on('mouseover', this.onViewOver, this);
47035         this.list.on('mousemove', this.onViewMove, this);
47036         this.list.on('scroll', this.onViewScroll, this);
47037         
47038         if(!this.tpl){
47039             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47040         }
47041         
47042         this.view = new Roo.View(this.list, this.tpl, {
47043             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47044         });
47045         
47046         this.view.on('click', this.onViewClick, this);
47047         
47048         this.store.on('beforeload', this.onBeforeLoad, this);
47049         this.store.on('load', this.onLoad, this);
47050         this.store.on('loadexception', this.onLoadException, this);
47051         
47052         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47053             "up" : function(e){
47054                 this.inKeyMode = true;
47055                 this.selectPrev();
47056             },
47057
47058             "down" : function(e){
47059                 if(!this.isExpanded()){
47060                     this.onTriggerClick();
47061                 }else{
47062                     this.inKeyMode = true;
47063                     this.selectNext();
47064                 }
47065             },
47066
47067             "enter" : function(e){
47068                 this.collapse();
47069                 
47070                 if(this.fireEvent("specialkey", this, e)){
47071                     this.onViewClick(false);
47072                 }
47073                 
47074                 return true;
47075             },
47076
47077             "esc" : function(e){
47078                 this.collapse();
47079             },
47080
47081             "tab" : function(e){
47082                 this.collapse();
47083                 
47084                 if(this.fireEvent("specialkey", this, e)){
47085                     this.onViewClick(false);
47086                 }
47087                 
47088                 return true;
47089             },
47090
47091             scope : this,
47092
47093             doRelay : function(foo, bar, hname){
47094                 if(hname == 'down' || this.scope.isExpanded()){
47095                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47096                 }
47097                 return true;
47098             },
47099
47100             forceKeyDown: true
47101         });
47102         
47103         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47104         
47105     },
47106     
47107     initNumberEvent : function(e)
47108     {
47109         this.inputEl().on("keydown" , this.fireKey,  this);
47110         this.inputEl().on("focus", this.onFocus,  this);
47111         this.inputEl().on("blur", this.onBlur,  this);
47112         
47113         this.inputEl().relayEvent('keyup', this);
47114         
47115         if(this.indicator){
47116             this.indicator.addClass('invisible');
47117         }
47118  
47119         this.originalValue = this.getValue();
47120         
47121         if(this.validationEvent == 'keyup'){
47122             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47123             this.inputEl().on('keyup', this.filterValidation, this);
47124         }
47125         else if(this.validationEvent !== false){
47126             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47127         }
47128         
47129         if(this.selectOnFocus){
47130             this.on("focus", this.preFocus, this);
47131             
47132         }
47133         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47134             this.inputEl().on("keypress", this.filterKeys, this);
47135         } else {
47136             this.inputEl().relayEvent('keypress', this);
47137         }
47138         
47139         var allowed = "0123456789";
47140         
47141         if(this.allowDecimals){
47142             allowed += this.decimalSeparator;
47143         }
47144         
47145         if(this.allowNegative){
47146             allowed += "-";
47147         }
47148         
47149         if(this.thousandsDelimiter) {
47150             allowed += ",";
47151         }
47152         
47153         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47154         
47155         var keyPress = function(e){
47156             
47157             var k = e.getKey();
47158             
47159             var c = e.getCharCode();
47160             
47161             if(
47162                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47163                     allowed.indexOf(String.fromCharCode(c)) === -1
47164             ){
47165                 e.stopEvent();
47166                 return;
47167             }
47168             
47169             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47170                 return;
47171             }
47172             
47173             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47174                 e.stopEvent();
47175             }
47176         };
47177         
47178         this.inputEl().on("keypress", keyPress, this);
47179         
47180     },
47181     
47182     onTriggerClick : function(e)
47183     {   
47184         if(this.disabled){
47185             return;
47186         }
47187         
47188         this.page = 0;
47189         this.loadNext = false;
47190         
47191         if(this.isExpanded()){
47192             this.collapse();
47193             return;
47194         }
47195         
47196         this.hasFocus = true;
47197         
47198         if(this.triggerAction == 'all') {
47199             this.doQuery(this.allQuery, true);
47200             return;
47201         }
47202         
47203         this.doQuery(this.getRawValue());
47204     },
47205     
47206     getCurrency : function()
47207     {   
47208         var v = this.currencyEl().getValue();
47209         
47210         return v;
47211     },
47212     
47213     restrictHeight : function()
47214     {
47215         this.list.alignTo(this.currencyEl(), this.listAlign);
47216         this.list.alignTo(this.currencyEl(), this.listAlign);
47217     },
47218     
47219     onViewClick : function(view, doFocus, el, e)
47220     {
47221         var index = this.view.getSelectedIndexes()[0];
47222         
47223         var r = this.store.getAt(index);
47224         
47225         if(r){
47226             this.onSelect(r, index);
47227         }
47228     },
47229     
47230     onSelect : function(record, index){
47231         
47232         if(this.fireEvent('beforeselect', this, record, index) !== false){
47233         
47234             this.setFromCurrencyData(index > -1 ? record.data : false);
47235             
47236             this.collapse();
47237             
47238             this.fireEvent('select', this, record, index);
47239         }
47240     },
47241     
47242     setFromCurrencyData : function(o)
47243     {
47244         var currency = '';
47245         
47246         this.lastCurrency = o;
47247         
47248         if (this.currencyField) {
47249             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47250         } else {
47251             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47252         }
47253         
47254         this.lastSelectionText = currency;
47255         
47256         //setting default currency
47257         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47258             this.setCurrency(this.defaultCurrency);
47259             return;
47260         }
47261         
47262         this.setCurrency(currency);
47263     },
47264     
47265     setFromData : function(o)
47266     {
47267         var c = {};
47268         
47269         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47270         
47271         this.setFromCurrencyData(c);
47272         
47273         var value = '';
47274         
47275         if (this.name) {
47276             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47277         } else {
47278             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47279         }
47280         
47281         this.setValue(value);
47282         
47283     },
47284     
47285     setCurrency : function(v)
47286     {   
47287         this.currencyValue = v;
47288         
47289         if(this.rendered){
47290             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47291             this.validate();
47292         }
47293     },
47294     
47295     setValue : function(v)
47296     {
47297         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47298         
47299         this.value = v;
47300         
47301         if(this.rendered){
47302             
47303             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47304             
47305             this.inputEl().dom.value = (v == '') ? '' :
47306                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47307             
47308             if(!this.allowZero && v === '0') {
47309                 this.hiddenEl().dom.value = '';
47310                 this.inputEl().dom.value = '';
47311             }
47312             
47313             this.validate();
47314         }
47315     },
47316     
47317     getRawValue : function()
47318     {
47319         var v = this.inputEl().getValue();
47320         
47321         return v;
47322     },
47323     
47324     getValue : function()
47325     {
47326         return this.fixPrecision(this.parseValue(this.getRawValue()));
47327     },
47328     
47329     parseValue : function(value)
47330     {
47331         if(this.thousandsDelimiter) {
47332             value += "";
47333             r = new RegExp(",", "g");
47334             value = value.replace(r, "");
47335         }
47336         
47337         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47338         return isNaN(value) ? '' : value;
47339         
47340     },
47341     
47342     fixPrecision : function(value)
47343     {
47344         if(this.thousandsDelimiter) {
47345             value += "";
47346             r = new RegExp(",", "g");
47347             value = value.replace(r, "");
47348         }
47349         
47350         var nan = isNaN(value);
47351         
47352         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47353             return nan ? '' : value;
47354         }
47355         return parseFloat(value).toFixed(this.decimalPrecision);
47356     },
47357     
47358     decimalPrecisionFcn : function(v)
47359     {
47360         return Math.floor(v);
47361     },
47362     
47363     validateValue : function(value)
47364     {
47365         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47366             return false;
47367         }
47368         
47369         var num = this.parseValue(value);
47370         
47371         if(isNaN(num)){
47372             this.markInvalid(String.format(this.nanText, value));
47373             return false;
47374         }
47375         
47376         if(num < this.minValue){
47377             this.markInvalid(String.format(this.minText, this.minValue));
47378             return false;
47379         }
47380         
47381         if(num > this.maxValue){
47382             this.markInvalid(String.format(this.maxText, this.maxValue));
47383             return false;
47384         }
47385         
47386         return true;
47387     },
47388     
47389     validate : function()
47390     {
47391         if(this.disabled || this.allowBlank){
47392             this.markValid();
47393             return true;
47394         }
47395         
47396         var currency = this.getCurrency();
47397         
47398         if(this.validateValue(this.getRawValue()) && currency.length){
47399             this.markValid();
47400             return true;
47401         }
47402         
47403         this.markInvalid();
47404         return false;
47405     },
47406     
47407     getName: function()
47408     {
47409         return this.name;
47410     },
47411     
47412     beforeBlur : function()
47413     {
47414         if(!this.castInt){
47415             return;
47416         }
47417         
47418         var v = this.parseValue(this.getRawValue());
47419         
47420         if(v || v == 0){
47421             this.setValue(v);
47422         }
47423     },
47424     
47425     onBlur : function()
47426     {
47427         this.beforeBlur();
47428         
47429         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47430             //this.el.removeClass(this.focusClass);
47431         }
47432         
47433         this.hasFocus = false;
47434         
47435         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47436             this.validate();
47437         }
47438         
47439         var v = this.getValue();
47440         
47441         if(String(v) !== String(this.startValue)){
47442             this.fireEvent('change', this, v, this.startValue);
47443         }
47444         
47445         this.fireEvent("blur", this);
47446     },
47447     
47448     inputEl : function()
47449     {
47450         return this.el.select('.roo-money-amount-input', true).first();
47451     },
47452     
47453     currencyEl : function()
47454     {
47455         return this.el.select('.roo-money-currency-input', true).first();
47456     },
47457     
47458     hiddenEl : function()
47459     {
47460         return this.el.select('input.hidden-number-input',true).first();
47461     }
47462     
47463 });/**
47464  * @class Roo.bootstrap.BezierSignature
47465  * @extends Roo.bootstrap.Component
47466  * Bootstrap BezierSignature class
47467  * This script refer to:
47468  *    Title: Signature Pad
47469  *    Author: szimek
47470  *    Availability: https://github.com/szimek/signature_pad
47471  *
47472  * @constructor
47473  * Create a new BezierSignature
47474  * @param {Object} config The config object
47475  */
47476
47477 Roo.bootstrap.BezierSignature = function(config){
47478     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47479     this.addEvents({
47480         "resize" : true
47481     });
47482 };
47483
47484 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47485 {
47486      
47487     curve_data: [],
47488     
47489     is_empty: true,
47490     
47491     mouse_btn_down: true,
47492     
47493     /**
47494      * @cfg {int} canvas height
47495      */
47496     canvas_height: '200px',
47497     
47498     /**
47499      * @cfg {float|function} Radius of a single dot.
47500      */ 
47501     dot_size: false,
47502     
47503     /**
47504      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47505      */
47506     min_width: 0.5,
47507     
47508     /**
47509      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47510      */
47511     max_width: 2.5,
47512     
47513     /**
47514      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47515      */
47516     throttle: 16,
47517     
47518     /**
47519      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47520      */
47521     min_distance: 5,
47522     
47523     /**
47524      * @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.
47525      */
47526     bg_color: 'rgba(0, 0, 0, 0)',
47527     
47528     /**
47529      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47530      */
47531     dot_color: 'black',
47532     
47533     /**
47534      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47535      */ 
47536     velocity_filter_weight: 0.7,
47537     
47538     /**
47539      * @cfg {function} Callback when stroke begin. 
47540      */
47541     onBegin: false,
47542     
47543     /**
47544      * @cfg {function} Callback when stroke end.
47545      */
47546     onEnd: false,
47547     
47548     getAutoCreate : function()
47549     {
47550         var cls = 'roo-signature column';
47551         
47552         if(this.cls){
47553             cls += ' ' + this.cls;
47554         }
47555         
47556         var col_sizes = [
47557             'lg',
47558             'md',
47559             'sm',
47560             'xs'
47561         ];
47562         
47563         for(var i = 0; i < col_sizes.length; i++) {
47564             if(this[col_sizes[i]]) {
47565                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47566             }
47567         }
47568         
47569         var cfg = {
47570             tag: 'div',
47571             cls: cls,
47572             cn: [
47573                 {
47574                     tag: 'div',
47575                     cls: 'roo-signature-body',
47576                     cn: [
47577                         {
47578                             tag: 'canvas',
47579                             cls: 'roo-signature-body-canvas',
47580                             height: this.canvas_height,
47581                             width: this.canvas_width
47582                         }
47583                     ]
47584                 },
47585                 {
47586                     tag: 'input',
47587                     type: 'file',
47588                     style: 'display: none'
47589                 }
47590             ]
47591         };
47592         
47593         return cfg;
47594     },
47595     
47596     initEvents: function() 
47597     {
47598         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47599         
47600         var canvas = this.canvasEl();
47601         
47602         // mouse && touch event swapping...
47603         canvas.dom.style.touchAction = 'none';
47604         canvas.dom.style.msTouchAction = 'none';
47605         
47606         this.mouse_btn_down = false;
47607         canvas.on('mousedown', this._handleMouseDown, this);
47608         canvas.on('mousemove', this._handleMouseMove, this);
47609         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47610         
47611         if (window.PointerEvent) {
47612             canvas.on('pointerdown', this._handleMouseDown, this);
47613             canvas.on('pointermove', this._handleMouseMove, this);
47614             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47615         }
47616         
47617         if ('ontouchstart' in window) {
47618             canvas.on('touchstart', this._handleTouchStart, this);
47619             canvas.on('touchmove', this._handleTouchMove, this);
47620             canvas.on('touchend', this._handleTouchEnd, this);
47621         }
47622         
47623         Roo.EventManager.onWindowResize(this.resize, this, true);
47624         
47625         // file input event
47626         this.fileEl().on('change', this.uploadImage, this);
47627         
47628         this.clear();
47629         
47630         this.resize();
47631     },
47632     
47633     resize: function(){
47634         
47635         var canvas = this.canvasEl().dom;
47636         var ctx = this.canvasElCtx();
47637         var img_data = false;
47638         
47639         if(canvas.width > 0) {
47640             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47641         }
47642         // setting canvas width will clean img data
47643         canvas.width = 0;
47644         
47645         var style = window.getComputedStyle ? 
47646             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47647             
47648         var padding_left = parseInt(style.paddingLeft) || 0;
47649         var padding_right = parseInt(style.paddingRight) || 0;
47650         
47651         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47652         
47653         if(img_data) {
47654             ctx.putImageData(img_data, 0, 0);
47655         }
47656     },
47657     
47658     _handleMouseDown: function(e)
47659     {
47660         if (e.browserEvent.which === 1) {
47661             this.mouse_btn_down = true;
47662             this.strokeBegin(e);
47663         }
47664     },
47665     
47666     _handleMouseMove: function (e)
47667     {
47668         if (this.mouse_btn_down) {
47669             this.strokeMoveUpdate(e);
47670         }
47671     },
47672     
47673     _handleMouseUp: function (e)
47674     {
47675         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47676             this.mouse_btn_down = false;
47677             this.strokeEnd(e);
47678         }
47679     },
47680     
47681     _handleTouchStart: function (e) {
47682         
47683         e.preventDefault();
47684         if (e.browserEvent.targetTouches.length === 1) {
47685             // var touch = e.browserEvent.changedTouches[0];
47686             // this.strokeBegin(touch);
47687             
47688              this.strokeBegin(e); // assume e catching the correct xy...
47689         }
47690     },
47691     
47692     _handleTouchMove: function (e) {
47693         e.preventDefault();
47694         // var touch = event.targetTouches[0];
47695         // _this._strokeMoveUpdate(touch);
47696         this.strokeMoveUpdate(e);
47697     },
47698     
47699     _handleTouchEnd: function (e) {
47700         var wasCanvasTouched = e.target === this.canvasEl().dom;
47701         if (wasCanvasTouched) {
47702             e.preventDefault();
47703             // var touch = event.changedTouches[0];
47704             // _this._strokeEnd(touch);
47705             this.strokeEnd(e);
47706         }
47707     },
47708     
47709     reset: function () {
47710         this._lastPoints = [];
47711         this._lastVelocity = 0;
47712         this._lastWidth = (this.min_width + this.max_width) / 2;
47713         this.canvasElCtx().fillStyle = this.dot_color;
47714     },
47715     
47716     strokeMoveUpdate: function(e)
47717     {
47718         this.strokeUpdate(e);
47719         
47720         if (this.throttle) {
47721             this.throttleStroke(this.strokeUpdate, this.throttle);
47722         }
47723         else {
47724             this.strokeUpdate(e);
47725         }
47726     },
47727     
47728     strokeBegin: function(e)
47729     {
47730         var newPointGroup = {
47731             color: this.dot_color,
47732             points: []
47733         };
47734         
47735         if (typeof this.onBegin === 'function') {
47736             this.onBegin(e);
47737         }
47738         
47739         this.curve_data.push(newPointGroup);
47740         this.reset();
47741         this.strokeUpdate(e);
47742     },
47743     
47744     strokeUpdate: function(e)
47745     {
47746         var rect = this.canvasEl().dom.getBoundingClientRect();
47747         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47748         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47749         var lastPoints = lastPointGroup.points;
47750         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47751         var isLastPointTooClose = lastPoint
47752             ? point.distanceTo(lastPoint) <= this.min_distance
47753             : false;
47754         var color = lastPointGroup.color;
47755         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47756             var curve = this.addPoint(point);
47757             if (!lastPoint) {
47758                 this.drawDot({color: color, point: point});
47759             }
47760             else if (curve) {
47761                 this.drawCurve({color: color, curve: curve});
47762             }
47763             lastPoints.push({
47764                 time: point.time,
47765                 x: point.x,
47766                 y: point.y
47767             });
47768         }
47769     },
47770     
47771     strokeEnd: function(e)
47772     {
47773         this.strokeUpdate(e);
47774         if (typeof this.onEnd === 'function') {
47775             this.onEnd(e);
47776         }
47777     },
47778     
47779     addPoint:  function (point) {
47780         var _lastPoints = this._lastPoints;
47781         _lastPoints.push(point);
47782         if (_lastPoints.length > 2) {
47783             if (_lastPoints.length === 3) {
47784                 _lastPoints.unshift(_lastPoints[0]);
47785             }
47786             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47787             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47788             _lastPoints.shift();
47789             return curve;
47790         }
47791         return null;
47792     },
47793     
47794     calculateCurveWidths: function (startPoint, endPoint) {
47795         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47796             (1 - this.velocity_filter_weight) * this._lastVelocity;
47797
47798         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47799         var widths = {
47800             end: newWidth,
47801             start: this._lastWidth
47802         };
47803         
47804         this._lastVelocity = velocity;
47805         this._lastWidth = newWidth;
47806         return widths;
47807     },
47808     
47809     drawDot: function (_a) {
47810         var color = _a.color, point = _a.point;
47811         var ctx = this.canvasElCtx();
47812         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47813         ctx.beginPath();
47814         this.drawCurveSegment(point.x, point.y, width);
47815         ctx.closePath();
47816         ctx.fillStyle = color;
47817         ctx.fill();
47818     },
47819     
47820     drawCurve: function (_a) {
47821         var color = _a.color, curve = _a.curve;
47822         var ctx = this.canvasElCtx();
47823         var widthDelta = curve.endWidth - curve.startWidth;
47824         var drawSteps = Math.floor(curve.length()) * 2;
47825         ctx.beginPath();
47826         ctx.fillStyle = color;
47827         for (var i = 0; i < drawSteps; i += 1) {
47828         var t = i / drawSteps;
47829         var tt = t * t;
47830         var ttt = tt * t;
47831         var u = 1 - t;
47832         var uu = u * u;
47833         var uuu = uu * u;
47834         var x = uuu * curve.startPoint.x;
47835         x += 3 * uu * t * curve.control1.x;
47836         x += 3 * u * tt * curve.control2.x;
47837         x += ttt * curve.endPoint.x;
47838         var y = uuu * curve.startPoint.y;
47839         y += 3 * uu * t * curve.control1.y;
47840         y += 3 * u * tt * curve.control2.y;
47841         y += ttt * curve.endPoint.y;
47842         var width = curve.startWidth + ttt * widthDelta;
47843         this.drawCurveSegment(x, y, width);
47844         }
47845         ctx.closePath();
47846         ctx.fill();
47847     },
47848     
47849     drawCurveSegment: function (x, y, width) {
47850         var ctx = this.canvasElCtx();
47851         ctx.moveTo(x, y);
47852         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47853         this.is_empty = false;
47854     },
47855     
47856     clear: function()
47857     {
47858         var ctx = this.canvasElCtx();
47859         var canvas = this.canvasEl().dom;
47860         ctx.fillStyle = this.bg_color;
47861         ctx.clearRect(0, 0, canvas.width, canvas.height);
47862         ctx.fillRect(0, 0, canvas.width, canvas.height);
47863         this.curve_data = [];
47864         this.reset();
47865         this.is_empty = true;
47866     },
47867     
47868     fileEl: function()
47869     {
47870         return  this.el.select('input',true).first();
47871     },
47872     
47873     canvasEl: function()
47874     {
47875         return this.el.select('canvas',true).first();
47876     },
47877     
47878     canvasElCtx: function()
47879     {
47880         return this.el.select('canvas',true).first().dom.getContext('2d');
47881     },
47882     
47883     getImage: function(type)
47884     {
47885         if(this.is_empty) {
47886             return false;
47887         }
47888         
47889         // encryption ?
47890         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47891     },
47892     
47893     drawFromImage: function(img_src)
47894     {
47895         var img = new Image();
47896         
47897         img.onload = function(){
47898             this.canvasElCtx().drawImage(img, 0, 0);
47899         }.bind(this);
47900         
47901         img.src = img_src;
47902         
47903         this.is_empty = false;
47904     },
47905     
47906     selectImage: function()
47907     {
47908         this.fileEl().dom.click();
47909     },
47910     
47911     uploadImage: function(e)
47912     {
47913         var reader = new FileReader();
47914         
47915         reader.onload = function(e){
47916             var img = new Image();
47917             img.onload = function(){
47918                 this.reset();
47919                 this.canvasElCtx().drawImage(img, 0, 0);
47920             }.bind(this);
47921             img.src = e.target.result;
47922         }.bind(this);
47923         
47924         reader.readAsDataURL(e.target.files[0]);
47925     },
47926     
47927     // Bezier Point Constructor
47928     Point: (function () {
47929         function Point(x, y, time) {
47930             this.x = x;
47931             this.y = y;
47932             this.time = time || Date.now();
47933         }
47934         Point.prototype.distanceTo = function (start) {
47935             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47936         };
47937         Point.prototype.equals = function (other) {
47938             return this.x === other.x && this.y === other.y && this.time === other.time;
47939         };
47940         Point.prototype.velocityFrom = function (start) {
47941             return this.time !== start.time
47942             ? this.distanceTo(start) / (this.time - start.time)
47943             : 0;
47944         };
47945         return Point;
47946     }()),
47947     
47948     
47949     // Bezier Constructor
47950     Bezier: (function () {
47951         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47952             this.startPoint = startPoint;
47953             this.control2 = control2;
47954             this.control1 = control1;
47955             this.endPoint = endPoint;
47956             this.startWidth = startWidth;
47957             this.endWidth = endWidth;
47958         }
47959         Bezier.fromPoints = function (points, widths, scope) {
47960             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47961             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47962             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47963         };
47964         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47965             var dx1 = s1.x - s2.x;
47966             var dy1 = s1.y - s2.y;
47967             var dx2 = s2.x - s3.x;
47968             var dy2 = s2.y - s3.y;
47969             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47970             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47971             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47972             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47973             var dxm = m1.x - m2.x;
47974             var dym = m1.y - m2.y;
47975             var k = l2 / (l1 + l2);
47976             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47977             var tx = s2.x - cm.x;
47978             var ty = s2.y - cm.y;
47979             return {
47980                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47981                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47982             };
47983         };
47984         Bezier.prototype.length = function () {
47985             var steps = 10;
47986             var length = 0;
47987             var px;
47988             var py;
47989             for (var i = 0; i <= steps; i += 1) {
47990                 var t = i / steps;
47991                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47992                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47993                 if (i > 0) {
47994                     var xdiff = cx - px;
47995                     var ydiff = cy - py;
47996                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47997                 }
47998                 px = cx;
47999                 py = cy;
48000             }
48001             return length;
48002         };
48003         Bezier.prototype.point = function (t, start, c1, c2, end) {
48004             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48005             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48006             + (3.0 * c2 * (1.0 - t) * t * t)
48007             + (end * t * t * t);
48008         };
48009         return Bezier;
48010     }()),
48011     
48012     throttleStroke: function(fn, wait) {
48013       if (wait === void 0) { wait = 250; }
48014       var previous = 0;
48015       var timeout = null;
48016       var result;
48017       var storedContext;
48018       var storedArgs;
48019       var later = function () {
48020           previous = Date.now();
48021           timeout = null;
48022           result = fn.apply(storedContext, storedArgs);
48023           if (!timeout) {
48024               storedContext = null;
48025               storedArgs = [];
48026           }
48027       };
48028       return function wrapper() {
48029           var args = [];
48030           for (var _i = 0; _i < arguments.length; _i++) {
48031               args[_i] = arguments[_i];
48032           }
48033           var now = Date.now();
48034           var remaining = wait - (now - previous);
48035           storedContext = this;
48036           storedArgs = args;
48037           if (remaining <= 0 || remaining > wait) {
48038               if (timeout) {
48039                   clearTimeout(timeout);
48040                   timeout = null;
48041               }
48042               previous = now;
48043               result = fn.apply(storedContext, storedArgs);
48044               if (!timeout) {
48045                   storedContext = null;
48046                   storedArgs = [];
48047               }
48048           }
48049           else if (!timeout) {
48050               timeout = window.setTimeout(later, remaining);
48051           }
48052           return result;
48053       };
48054   }
48055   
48056 });
48057
48058  
48059
48060  // old names for form elements
48061 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48062 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48063 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48064 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48065 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48066 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48067 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48068 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48069 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48070 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48071 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48072 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48073 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48074 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48075 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48076 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48077 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48078 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48079 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48080 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48081 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48082 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48083 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48084 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48085 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48086 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48087
48088 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48089 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48090
48091 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48092 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48093
48094 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48095 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48096 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48097 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48098